Note: This project involves getting data ready for analysis and doing some preliminary investigations. Project 2 will involve modeling and predictions, and will be released at a later date. Both projects will have equal weightage towards your grade.

Data

In this project, you will explore a dataset that contains information about movies, including ratings, budget, gross revenue and other attributes. It was prepared by Dr. Guy Lebanon, and here is his description of the dataset:

The file movies_merged contains a dataframe with the same name that has 40K rows and 39 columns. Each row represents a movie title and each column represents a descriptor such as Title, Actors, and Budget. I collected the data by querying IMDb’s API (see www.omdbapi.com) and joining it with a separate dataset of movie budgets and gross earnings (unknown to you). The join key was the movie title. This data is available for personal use, but IMDb’s terms of service do not allow it to be used for commercial purposes or for creating a competing repository.

Objective

Your goal is to investigate the relationship between the movie descriptors and the box office success of movies, as represented by the variable Gross. This task is extremely important as it can help a studio decide which titles to fund for production, how much to bid on produced movies, when to release a title, how much to invest in marketing and PR, etc. This information is most useful before a title is released, but it is still very valuable after the movie is already released to the public (for example it can affect additional marketing spend or how much a studio should negotiate with on-demand streaming companies for “second window” streaming rights).

Instructions

This is an R Markdown Notebook. Open this file in RStudio to get started.

When you execute code within the notebook, the results appear beneath the code. Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Cmd+Shift+Enter.

x = 1:10
print(x^2)
 [1]   1   4   9  16  25  36  49  64  81 100

Plots appear inline too:

plot(x, x^2, 'o')

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Cmd+Option+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Cmd+Shift+K to preview the HTML file).

Please complete the tasks below and submit this R Markdown file (as pr1.Rmd) as well as a PDF export of it (as pr1.pdf). Both should contain all the code, output, plots and written responses for each task.

Setup

Load data

Make sure you’ve downloaded the movies_merged file and it is in the current working directory. Now load it into memory:

load('movies_merged')

This creates an object of the same name (movies_merged). For convenience, you can copy it to df and start using it:

df = movies_merged
cat("Dataset has", dim(df)[1], "rows and", dim(df)[2], "columns", end="\n", file="")
Dataset has 40789 rows and 39 columns 
colnames(df)
 [1] "Title"             "Year"              "Rated"             "Released"          "Runtime"           "Genre"            
 [7] "Director"          "Writer"            "Actors"            "Plot"              "Language"          "Country"          
[13] "Awards"            "Poster"            "Metascore"         "imdbRating"        "imdbVotes"         "imdbID"           
[19] "Type"              "tomatoMeter"       "tomatoImage"       "tomatoRating"      "tomatoReviews"     "tomatoFresh"      
[25] "tomatoRotten"      "tomatoConsensus"   "tomatoUserMeter"   "tomatoUserRating"  "tomatoUserReviews" "tomatoURL"        
[31] "DVD"               "BoxOffice"         "Production"        "Website"           "Response"          "Budget"           
[37] "Domestic_Gross"    "Gross"             "Date"             

Load R packages

Load any R packages that you will need to use. You can come back to this chunk, edit it and re-run to load any additional packages later.

library(ggplot2)
library(GGally)
library(scales)

If you are loading any non-standard packages (ones that have not been discussed in class or explicitly allowed for this project), please mention them below. Include any special instructions if they cannot be installed using the regular install.packages('<pkg name>') command.

Non-standard packages used: None

Tasks

Each task below is worth 10 points, and is meant to be performed sequentially, i.e. do step 2 after you have processed the data as described in step 1. Total points: 100

Complete each task by implementing code chunks as described by TODO comments, and by responding to questions (“Q:”) with written answers (“A:”). If you are unable to find a meaningful or strong relationship in any of the cases when requested, explain why not by referring to appropriate plots/statistics.

It is OK to handle missing values below by omission, but please omit as little as possible. It is worthwhile to invest in reusable and clear code as you may need to use it or modify it in project 2.

1. Remove non-movie rows

The variable Type captures whether the row is a movie, a TV series, or a game. Remove all rows from df that do not correspond to movies.

# TODO: Remove all rows from df that do not correspond to movies
df <- subset(df, Type == "movie")

Q: How many rows are left after removal? Enter your response below.

A: 40,000

2. Process Runtime column

The variable Runtime represents the length of the title as a string. Write R code to convert it to a numeric value (in minutes) and replace df$Runtime with the new numeric column.

# TODO: Replace df$Runtime with a numeric column containing the runtime in minutes
df$Runtime = as.numeric(gsub('\\D','',df$Runtime))

Now investigate the distribution of Runtime values and how it changes over years (variable Year, which you can bucket into decades) and in relation to the budget (variable Budget). Include any plots that illustrate.

# TODO: Investigate the distribution of Runtime values and how it varies by Year and Budget
df$Decade = cut(df$Year, breaks = seq(1880,2020, by = 10), labels = paste(as.character(seq(1880, 2010, by = 10)),'s',sep = ''))
# Distribution of Runtime
ggplot(df, aes(x = Runtime)) + 
  geom_histogram(bins = 30, color = 'gold', fill = 'black') + 
  xlim(0,300) + labs(title = 'Runtime Histogram') +
  theme(panel.background = element_rect(fill = 'darkturquoise'))

# Budget/Runtime/Year Relationship Plot
ggplot(df, aes(x = Budget, y = Runtime, color = Decade)) + 
  geom_point(size = 1, stat = 'summary', fun.y = median) +
  labs(title = 'Runtime vs Budget by Decades')

Q: Comment on the distribution as well as relationships. Are there any patterns or trends that you can observe?

A: The distribution of Runtimes is a somewhat normal distribution where most of the runtimes are slightly less than 100 minutes long. After looking at the more indepth relationship between Runtime, Budget, and Year (by Decade), the first thing to point out was that several of the movies made prior to the 1970s did not have Budget data stored. With this taken into account, a noticeable trend was that the runtimes for movies from the 1990s until now have generally shorter runtimes. Also, these movies have a much wider range in Budget. On the other hand, zero movies made before the 1990s had a budget exceeding $80,000,000.

3. Encode Genre column

The column Genre represents a list of genres associated with the movie in a string format. Write code to parse each text string into a binary vector with 1s representing the presence of a genre and 0s the absence, and add it to the dataframe as additional columns. Then remove the original Genre column.

For example, if there are a total of 3 genres: Drama, Comedy, and Action, a movie that is both Action and Comedy should be represented by a binary vector <0, 1, 1>. Note that you need to first compile a dictionary of all possible genres and then figure out which movie has which genres (you can use the R tm package to create the dictionary).

# TODO: Replace Genre with a collection of binary columns
uniq_genre_groups = unique(df$Genre)
indiGenres = rep(0, times = 0)
for(i in seq(1,length(uniq_genre_groups))){
  indiGenres = append(indiGenres, unlist(strsplit(uniq_genre_groups[i], split = ', ')))
}
indiGenres = sort(unique(indiGenres))
initBinGenreVector = rep(0, times = length(indiGenres))
Genre = list()
for(i in seq(1,dim(df)[1])){
  Genre[[i]] = initBinGenreVector
  for(j in seq(1,length(indiGenres))){
    if(grepl(indiGenres[j], df$Genre[i])){
      Genre[[i]][j] = 1
    }
  }
}
df$Genre = Genre
rm(uniq_genre_groups,Genre, i, j)

Plot the relative proportions of movies having the top 10 most common genres.

# TODO: Select movies from top 10 most common genres and plot their relative proportions
genre_cnts = initBinGenreVector
for(i in seq(1,dim(df)[1])){
  genre_cnts = genre_cnts + df$Genre[[i]]
}
rankedGenres = rank(-genre_cnts)
Genres = data.frame(Genre = indiGenres, Rank = rankedGenres, Count = genre_cnts)
Genres$MoviePercentage = round(Genres$Count/dim(df)[1], 3)*100
rm(rankedGenres, genre_cnts, indiGenres, i)
ggplot(data = Genres[Genres$Rank <= 10,], aes(x = Genre, y = MoviePercentage, color = Genre)) +
  geom_bar(stat = 'identity') +
  geom_text(aes(label = MoviePercentage), vjust = 1.5) +
  #ylim(0,100) +
  labs(title = 'Top 10 Most Common Genres', x = 'Genre', y = 'Percentage (%) of Total Movies with Genre') +
  theme(axis.text.x = element_blank())

Examine how the distribution of Runtime changes across genres for the top 10 most common genres.

# TODO: Plot Runtime distribution for top 10 most common genres
N = 200000
x = 0
runTimeTopGenre = data.frame(Genre = rep('', N), Runtime = rep(NA, N))
runTimeTopGenre$Genre = as.character(runTimeTopGenre$Genre)
Genres$Genre = as.character(Genres$Genre)
# Roughly 8 min runtime
for(i in seq(1,dim(df)[1])){
  for(j in seq(1,dim(Genres)[1])){
    if(df$Genre[[i]][j] == 1 & j %in% row.names(Genres[Genres$Rank <= 11, ])){
      x = x + 1
      runTimeTopGenre$Genre[x] = Genres$Genre[j]
      runTimeTopGenre$Runtime[x] = df$Runtime[i]
    }
  }
}
runTimeTopGenre = runTimeTopGenre[!apply(is.na(runTimeTopGenre) | runTimeTopGenre == '', 1, all),]
rm(i, j, N, x)
df_3 = df
ggplot(runTimeTopGenre, aes(x = Genre, y = Runtime, fill = Genre)) +
  geom_boxplot() +
  ylim(0,350) +
  labs(title = 'Top 10 Genres: Runtime Distribution Box Plots', x = 'Genre', y = 'Runtime (min)') +
  theme(axis.text.x = element_blank())

Q: Describe the interesting relationship(s) you observe. Are there any expected or unexpected trends that are evident?

A: ‘Drama’ genres were the most popular, representing almost 40% of the movies. I found that ‘Thriller’ and ‘Animation’ films (representing 8.4% and 7% of the movies respectively) were noticeably shorter than the rest of the top 10 Genres. Also, ‘Animation’ films had such a small interquartile range (narrow concentrated distribution) that you can barely see the box. ‘Documentary’ films (representing 7.6% of the movies) had the largest interquartile range meaning that it had the widest concentrated distribution. There are also plenty of outliers. I cut the Runtime range to less than 350 minutes, but there were several of stragglers outside that range.

4. Eliminate mismatched rows

The dataframe was put together by merging two different sources of data and it is possible that the merging process was inaccurate in some cases (the merge was done based on movie title, but there are cases of different movies with the same title). The first source’s release time was represented by the column Year (numeric representation of the year) and the second by the column Released (string representation of release date).

Find and remove all rows where you suspect a merge error occurred based on a mismatch between these two variables. To make sure subsequent analysis and modeling work well, avoid removing more than 10% of the rows that have a Gross value present.

# TODO: Remove rows with Released-Year mismatch
# Remove all mismatches that do not havea  Gross values
mismatchNAgross_ind = (!is.na(df$Released) & is.na(df$Gross) & abs(as.numeric(format(df$Released, '%Y')) - df$Year) > 0)
df2 = df[!mismatchNAgross_ind,]
print(paste('Number of Rows removed without Gross Values =', dim(df)[1] - dim(df2)[1]))
[1] "Number of Rows removed without Gross Values = 4957"
numWithGross = sum(!is.na(df$Gross))
tenPerGross = numWithGross/10
diff = -1
NUM = tenPerGross + 1
while(NUM > tenPerGross){
  diff = diff + 1
  NUM = sum(!is.na(df2$Released) & !is.na(df2$Gross) & abs(as.numeric(format(df2$Released, '%Y')) - df2$Year) > diff)
  mismatchWgross_ind = (!is.na(df2$Released) & !is.na(df2$Gross) & abs(as.numeric(format(df2$Released, '%Y')) - df2$Year) > diff)
}
df3 = df2[!mismatchWgross_ind,]
print(paste(round(NUM/numWithGross*100, 2), '% of Gross valued records removed', sep = ''))
[1] "2% of Gross valued records removed"
print(paste('Number of Rows removed with Gross Values =', dim(df2)[1] - dim(df3)[1]))
[1] "Number of Rows removed with Gross Values = 91"
print(paste('Total number of rows removed =', dim(df)[1] - dim(df3)[1]))
[1] "Total number of rows removed = 5048"
df = df3
rm(diff, df2, df3, mismatchNAgross_ind, mismatchWgross_ind, NUM, numWithGross, tenPerGross)
df_4 = df

Q: What is your precise removal logic and how many rows did you end up removing?

A: The first thing I did was ignore any NA values in the ‘Released’ column. Next thing I did was remove all rows which ‘Gross’ was missing values (NA) and the years did not match up between ‘Year’ and ‘Released’. The next step was to remove the rows that did not match up between ‘Year’ and ‘Released’ without surpasing 10% of the records with Gross values. To do this, I checked the difference in years between ‘Year’ and ‘Released’ incrimentally while keeping track of how many records would be removed. It iterates (increasing the difference threshold value each time) until the sum of the number of rows that were to be removed is less than 10% of the rows with Gross values. This process ends up removing rows that have ‘Year’ and ‘Released’ differences greater than 1 (Ex: diff(2004,2006) > 1). After all of this is said and done, 5048 rows were removed in the process.

5. Explore Gross revenue

For the commercial success of a movie, production houses want to maximize Gross revenue. Investigate if Gross revenue is related to Budget, Runtime or Genre in any way.

Note: To get a meaningful relationship, you may have to partition the movies into subsets such as short vs. long duration, or by genre, etc.

# TODO: Investigate if Gross Revenue is related to Budget, Runtime or Genre
df_5 = df[!is.na(df$Gross),]
df_5$MonthReleased = as.numeric(format(df_5$Released, '%m'))
N = 20000
x = 0
gross = data.frame(Genre = rep('', N), Runtime = rep(NA, N), Budget = rep(NA, N), Gross = rep(NA, N))
gross$Genre = as.character(gross$Genre)
# Roughly 2 min runtime
for(i in seq(1,dim(df_5)[1])){
  for(j in seq(1,dim(Genres)[1])){
    if(df_5$Genre[[i]][j] == 1 & j %in% row.names(Genres[Genres$Rank <= 11, ])){
      x = x + 1
      gross$Genre[x] = Genres$Genre[j]
      gross$Runtime[x] = df_5$Runtime[i]
      gross$Budget[x] = df_5$Budget[i]
      gross$Gross[x] = df_5$Gross[i]
      gross$MonthReleased[x] = df_5$MonthReleased[i]
    }
  }
}
gross = gross[!apply(is.na(gross) | gross == '', 1, all),]
rm(i, j, N, x)
gross$RunTime = cut(gross$Runtime, breaks = seq(0,220, by = 20), 
                labels = paste(as.character(seq(0, 200, by = 20)),'min'))
ggplot(gross, aes(x = Budget, y = Gross)) + 
  facet_wrap(~ Genre) +
  geom_point(size = 0.7) +
  geom_smooth() +
  xlim(0,2e8) + ylim(0,1.5e9)

ggplot(gross, aes(x = Budget, y = Gross, color = RunTime)) + 
  facet_wrap(~ Genre) +
  geom_point(size = 0.7) +
  xlim(0,2e8) + ylim(0,1.5e9)

ggplot(gross, aes(x = Genre, y = Gross, fill = Genre)) +
  geom_boxplot() +
  ylim(0,1.5e9) +
  labs(title = 'Gross Distribution Box Plots by Genre', x = 'Genre', y = 'Gross ($)') +
  theme(axis.text.x = element_blank())

ggplot(gross, aes(x = RunTime, y = Gross, fill = RunTime)) +
  geom_boxplot() +
  ylim(0,1.5e9) +
  labs(title = 'Gross Distribution Box Plots by RunTime', x = 'RunTime', y = 'Gross ($)') +
  theme(axis.text.x = element_blank())

Q: Did you find any observable relationships or combinations of Budget/Runtime/Genre that result in high Gross revenue? If you divided the movies into different subsets, you may get different answers for them - point out interesting ones.

A: The results I found from the above graphs were interesting. It seems for all Genres, there is a positive correlation between increasing the Budget and increased Gross revenue. From the middle two plots, you can see that Animation and Adventure Genres historically has potential to produce higher gross revenues than the rest of the genres. Also, observing runtimes in the second and last plot, you can see a sort of bell curve with peak gross revenues around 140 min to 160 min.

# TODO: Investigate if Gross Revenue is related to Release Month
ggplot(gross, aes(x = as.factor(MonthReleased), y = Gross, color = MonthReleased)) +
  geom_boxplot() +
  ylim(0,1.5e9) +
  scale_colour_gradient(low = 'red', high = 'blue') +
  labs(title = 'Gross Distribution Box Plots by Release Month', x = 'Release Month', y = 'Gross ($)')

A: It seems like the highest grossing movies are released in early summer with a notebale mention during the holidays (last two months of the year).

6. Process Awards column

The variable Awards describes nominations and awards in text format. Convert it to 2 numeric columns, the first capturing the number of wins, and the second capturing nominations. Replace the Awards column with these new columns, and then study the relationship of Gross revenue with respect to them.

Note that the format of the Awards column is not standard; you may have to use regular expressions to find the relevant values. Try your best to process them, and you may leave the ones that don’t have enough information as NAs or set them to 0s.

# TODO: Convert Awards to 2 numeric columns: wins and nominations
df_6 = df
df_6$Wins = rep(0, times = dim(df_6)[1])
df_6$Nominations = rep(0, times = dim(df_6)[1])
for(i in seq(1:dim(df_6)[1])){
  wins = 0
  nominations = 0
  if(grepl("Won ", df_6$Awards[i])){
    wins = wins + as.numeric(gsub(".*Won(\\s)\\b(\\d+).*",
                                  "\\2", df_6$Awards[i], perl=TRUE))
  }
  if(grepl(" win", df_6$Awards[i])){
    wins = wins + as.numeric(gsub(".*\\b(\\d+)(\\s)win.*",
                                  "\\1", df_6$Awards[i], perl=TRUE))
  }
  if(grepl("Nominated for ", df_6$Awards[i])){
    nominations = nominations + as.numeric(gsub(".*Nominated for(\\s)\\b(\\d+).*",
                                                "\\2", df_6$Awards[i], perl=TRUE))
  }
  if(grepl(" nomination", df_6$Awards[i])){
    nominations = nominations + as.numeric(gsub(".*\\b(\\d+)(\\s)nomination.*",
                                                "\\1", df_6$Awards[i], perl=TRUE))
  }
  df_6$Wins[i] = wins
  df_6$Nominations[i] = nominations
}
df_6$Awards <- NULL
rm(i,wins,nominations)
# Calculate Non-zero wins/nominations
print(paste("Number of movies with non-zero wins =",sum(df_6$Wins != 0)))
[1] "Number of movies with non-zero wins = 9071"
print(paste("Number of movies with non-zero nominations =",sum(df_6$Nominations != 0)))
[1] "Number of movies with non-zero nominations = 9534"

Q: How did you construct your conversion mechanism? How many rows had valid/non-zero wins or nominations?

A: First I used the data frame we used after concluding question 4. Next I created the columns ‘Wins’ and ‘Nominations’. From here I looped through the dataframe searching for 4 different substrings: “Won”, “Nominated for”, " win“,” nomination“. If the grepl fuction returned true on these strings, then I used the gsub function to extract the number before or after this string depending on the string. I used this number to add to either wins or nominations then added the values to the new columns. If none of the strings were found, the values remained at zero. Finally, after defining the new columns with the extracted values, I removed the old ‘Awards’ column. I found that there were 9071 movies with non-zero wins and 9534 movies with non-zero nominations out of the original 34,952 movies.

# TODO: Plot Gross revenue against wins and nominations
ggplot(df_6, aes(x = Number, y = Gross, color = Number)) +
  geom_point(aes(x = Nominations, col = "Nominations"), size = 0.7) +
  geom_point(aes(x = Wins, col = "Wins"), size = 0.7) +
  xlim(0,200) + ylim(0,1.5e9) +
  labs(title = 'Gross Revenue ($) vs Wins/Nominations')

Q: How does the gross revenue vary by number of awards won and nominations received?

A: After looking at both Nominations and Wins individually, I have found that there is not much of a correlation to my surpise. Even after taking into account that there are several movies that received no awards, plenty of these movies seemed to gross a lot of money. In fact, in many cases, these movies grossed around the same amount (give or take) as those movies who received a plethora of awards.

7. Movie ratings from IMDb and Rotten Tomatoes

There are several variables that describe ratings, including IMDb ratings (imdbRating represents average user ratings and imdbVotes represents the number of user ratings), and multiple Rotten Tomatoes ratings (represented by several variables pre-fixed by tomato). Read up on such ratings on the web (for example rottentomatoes.com/about and www.imdb.com/help/show_leaf?votestopfaq).

Investigate the pairwise relationships between these different descriptors using graphs.

# TODO: Illustrate how ratings from IMDb and Rotten Tomatoes are related
df = df_6
ggplot(df, aes(x = imdbRating, y = tomatoMeter, color = tomatoUserMeter)) + 
  geom_point(size = 0.7) +
  geom_smooth() +
  labs(title = 'Tomato Meter (Critic Score) vs IMDB Rating (User Score)')

boxplot(df$tomatoMeter, main = 'Rotten Tomatoes Critics Meter Box Plot')

boxplot(df$imdbRating, main = 'Rotten Tomatoes Critics Meter Box Plot')

print(paste('Critics mean =',mean(df$tomatoMeter, na.rm = TRUE)))
[1] "Critics mean = 57.4262536873156"
print(paste('Critics median =',median(df$tomatoMeter, na.rm = TRUE)))
[1] "Critics median = 60"
print(paste('Users mean =',mean(df$imdbRating, na.rm = TRUE)))
[1] "Users mean = 6.23529516095658"
print(paste('Users median =',median(df$imdbRating, na.rm = TRUE)))
[1] "Users median = 6.4"

Q: Comment on the similarities and differences between the user ratings of IMDb and the critics ratings of Rotten Tomatoes.

A: After doing some research, I found out that IMDB ratings are based off of a rating system where movies are rated by IMDB members. Anyone can become an IMDB member and vote. Rotten Tomatoes is different. Rotten Tomatoes has two main aggregated scores used to rate movies: the TomatoMeter and the Audience User Meter. The TomatoMeter rating is a “trusted measurement of movie and TV programming quaility” rated by professional film and television critics. Since I we are asked to review the similarities and differences between the IMDB user ratings and the Rotten Tomatoes critics ratings, I plotted ‘tomatoMeter’ vs ‘imdbRating’. For fun, I also threw in the Audience User Meter scores (tomatoUserMeter) represented as a filled color (see legend in graph above). A similarity I found in the relationship was that there was an obvious positive correlation between user and critic ratings. There were plenty if interesting differences also. It seems the user ratings are typically conservative; meaning that the majority of these ratings are between 55% and 75% of the maximum rating. On the other hand, Rotten Tomatoes scores have a majority range between 30% and 80% the maximum rating. That is over double the range. There are no movies with a 0 or perfect rating in IMDB and there are several of movies with 0 or perfect ratings in critics ratings. Also, based on the mean and median values above, users are typically “nicer” in terms of giving movies high ratings while the data shows that critics are a bit tougher. You can also see this when analyzing the first plot as the mean curve is shifted to the right of center, representing higher average user ratings than critic ratings.

8. Ratings and awards

These ratings typically reflect the general appeal of the movie to the public or gather opinions from a larger body of critics. Whereas awards are given by professional societies that may evaluate a movie on specific attributes, such as artistic performance, screenplay, sound design, etc.

Study the relationship between ratings and awards using graphs (awards here refers to wins and/or nominations).

# TODO: Show how ratings and awards are related
base_breaks <- function(n = 10){
  function(x){
    axisTicks(log10(range(x, na.rm = TRUE)), log = TRUE, n = n)
  }
}
breaks <- axTicks(side = 2)
ggplot(df, aes(x = imdbRating, y = Wins)) + 
  geom_point(size = 0.7) + 
  scale_y_continuous(trans = log_trans(), breaks = base_breaks(),
                      labels = prettyNum) +
  geom_smooth() +
  labs(title = 'Wins vs User Ratings')

ggplot(df, aes(x = tomatoMeter, y = Wins)) + 
  geom_point(size = 0.7) + 
  scale_y_continuous(trans = log_trans(), breaks = base_breaks(),
                      labels = prettyNum) +
  geom_smooth() +
  labs(title = 'Wins vs Critic Ratings')

ggplot(df, aes(x = imdbRating, y = Nominations)) + 
  geom_point(size = 0.7) + 
  scale_y_continuous(trans = log_trans(), breaks = base_breaks(),
                      labels = prettyNum) +
  geom_smooth() +
  labs(title = 'Nominations vs User Ratings')

ggplot(df, aes(x = tomatoMeter, y = Nominations)) + 
  geom_point(size = 0.7) + 
  scale_y_continuous(trans = log_trans(), breaks = base_breaks(),
                      labels = prettyNum) +
  geom_smooth() +
  labs(title = 'Nominations vs Critic Ratings')
par(old.par)

Q: How good are these ratings in terms of predicting the success of a movie in winning awards or nominations? Is there a high correlation between two variables?

A: At first I made these plots on the linear scale and did not see much of a correlation. When I changed the y axis to the log scale, I noticed a little more of correleation. I first thought The ratings didn’t do as good a job predicting award wins or nominations. But I did believe the opposite. If a movie had at least 30 or so awards, then it will most likely have a very good rating (User and Critic rating). The same can be said if a movie has at least 60 or so nominations. After changing the plots to x-log scale, I noticed a slight correlation. If the imdbRating is greater than 7.5, it means these movies have more than two times the number of wins and nominations than those who have a 5.0 rating. The critics scores had an even higher correlation (for the most part). If the Meter rating was higher than 80, it had almost 3 times more awards an average than movies which had ratings lower than 50. The similar correlation can be interpretted when looking at nominations. An interesting trend was that once the Meter score reached 90%, the number of wins and nominations peaked on average and then proceeded to decline as the meter score rose. I wouldn’t go and say these ratings are good at predicting success of a movie winning awards or nominations, but I would defintely say higher ratings mean a much more likelyhood of success on an awards category.

9. Expected insights

Come up with two new insights (backed up by data and graphs) that is expected. Here “new” means insights that are not an immediate consequence of one of the above tasks. You may use any of the columns already explored above or a different one in the dataset, such as Title, Actors, etc.

# TODO: Find and illustrate two expected insights
# Popularly known ratings
sum_popRatings = sum(df$Rated == "G" | df$Rated == "PG" | df$Rated == "PG-13" | df$Rated == "R")
Rate_ind = df$Rated == "G" | df$Rated == "PG" | df$Rated == "PG-13" | df$Rated == "R"
Rate = df[Rate_ind,]
ggplot(Rate, aes(x = Rated, y = Gross, fill = Rated)) +
  geom_boxplot() +
  ylim(0,5e8) +
  labs(title = 'Gross vs Popularly Rated Distribution Box Plots')

ggplot(Rate, aes(x = Rated, y = Runtime, fill = Rated)) +
  geom_boxplot() +
  ylim(0,250) +
  labs(title = 'Runtime vs Popularly Rated Distribution Box Plots')

print(paste("Percentage of movies with popular ratings (G, PG, PG-13, R) from original movie dataset =",
            round(sum_popRatings/dim(df_3)[1], 3)*100, "%"))
[1] "Percentage of movies with popular ratings (G, PG, PG-13, R) from original movie dataset = 25.7 %"

Q: Expected insight #1.

A: Almost surprisingly, less than 30% of the movies (from df dataset) are rated G, PG, PG-13, or R which has been considered the norm of a movie ratings when released in movie theaters… But, as expected, I did not believe the rating of the movie really affected the runtimes. These runtimes were used to analyze Gross revenue. And consistent with the releationship between runtimes and gross revenue, rated did not have an obvious impact on gross revenue nor runtime. It is interesting that these obvious finds lead to unobvious findings. The distributions for explicit movies have a narrower Gross distribution than more ‘family’ movies.

ggplot(Rate, aes(x = Rated, y = Year, fill = Rated)) +
  geom_boxplot() +
  labs(title = 'Year vs Popularly Rated Distribution Box Plots')

ggplot(Rate, aes(x = Year, y = Wins, color = Rated)) +
  geom_point(size = 0.7) +
  scale_y_continuous(trans = log_trans(), breaks = base_breaks(),
                      labels = prettyNum) +
  xlim(1950, 2017) +
  labs(title = 'Wins vs Year (Rated)')

Q: Expected insight #2.

A: As expected, PG-13 movies have the most ‘recent’ year distribution. The box plot shows this clearly. PG-13 movies are relatively new. Also, in terms of number of wins, most of the movies which have won awards since 1990 are rated PG-13 or R. Before PG-13 become more popular, most of the wins were by movies rated PG or R. Also, this explains that if you want to win awards, it is much more difficult to do with a G rated movie (sorry kids).

10. Unexpected insight

Come up with one new insight (backed up by data and graphs) that is unexpected at first glance and do your best to motivate it. Same instructions apply as the previous task.

# TODO: Find and illustrate one unexpected insight
ggplot(df, aes(x = Year)) +
  geom_histogram(bins = 30, color = 'gold', fill = 'black') +
  labs(title = 'Year Released Histogram') +
  theme(panel.background = element_rect(fill = 'darkturquoise'))

Q: Unexpected insight.

A: At first sight, I beleived this to be an expected insight because I thought the number of movies made would increase as time passed especially since movies tend to continue to break records at the box office and make more money. But to my surprise, it is not quite obvious. Sure there was a spike in the late 1990s and early 2000s, but if you exclude that time period, the number of movies released per time period is roughly the same after World War II era.

LS0tCnRpdGxlOiAnUHJvamVjdCAxOiBFeHBsb3JlIGFuZCBQcmVwYXJlIERhdGEgLSBkZ2lyb24zJwpzdWJ0aXRsZTogfC0KICBDU0U2MjQyIC0gRGF0YSBhbmQgVmlzdWFsIEFuYWx5dGljcyAtIFNwcmluZyAyMDE3CiAgRHVlOiBTdW5kYXksIE1hcmNoIDUsIDIwMTcgYXQgMTE6NTkgUE0gVVRDLTEyOjAwIG9uIFQtU3F1YXJlCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdAotLS0KCl9Ob3RlOiBUaGlzIHByb2plY3QgaW52b2x2ZXMgZ2V0dGluZyBkYXRhIHJlYWR5IGZvciBhbmFseXNpcyBhbmQgZG9pbmcgc29tZSBwcmVsaW1pbmFyeSBpbnZlc3RpZ2F0aW9ucy4gUHJvamVjdCAyIHdpbGwgaW52b2x2ZSBtb2RlbGluZyBhbmQgcHJlZGljdGlvbnMsIGFuZCB3aWxsIGJlIHJlbGVhc2VkIGF0IGEgbGF0ZXIgZGF0ZS4gQm90aCBwcm9qZWN0cyB3aWxsIGhhdmUgZXF1YWwgd2VpZ2h0YWdlIHRvd2FyZHMgeW91ciBncmFkZS5fCgojIERhdGEKCkluIHRoaXMgcHJvamVjdCwgeW91IHdpbGwgZXhwbG9yZSBhIGRhdGFzZXQgdGhhdCBjb250YWlucyBpbmZvcm1hdGlvbiBhYm91dCBtb3ZpZXMsIGluY2x1ZGluZyByYXRpbmdzLCBidWRnZXQsIGdyb3NzIHJldmVudWUgYW5kIG90aGVyIGF0dHJpYnV0ZXMuIEl0IHdhcyBwcmVwYXJlZCBieSBEci4gR3V5IExlYmFub24sIGFuZCBoZXJlIGlzIGhpcyBkZXNjcmlwdGlvbiBvZiB0aGUgZGF0YXNldDoKCj4gVGhlIGZpbGUgW2Btb3ZpZXNfbWVyZ2VkYF0oaHR0cHM6Ly9zMy5hbWF6b25hd3MuY29tL2NvbnRlbnQudWRhY2l0eS1kYXRhLmNvbS9jb3Vyc2VzL2d0LWNzNjI0Mi9wcm9qZWN0L21vdmllc19tZXJnZWQpIGNvbnRhaW5zIGEgZGF0YWZyYW1lIHdpdGggdGhlIHNhbWUgbmFtZSB0aGF0IGhhcyA0MEsgcm93cyBhbmQgMzkgY29sdW1ucy4gRWFjaCByb3cgcmVwcmVzZW50cyBhIG1vdmllIHRpdGxlIGFuZCBlYWNoIGNvbHVtbiByZXByZXNlbnRzIGEgZGVzY3JpcHRvciBzdWNoIGFzIGBUaXRsZWAsIGBBY3RvcnNgLCBhbmQgYEJ1ZGdldGAuIEkgY29sbGVjdGVkIHRoZSBkYXRhIGJ5IHF1ZXJ5aW5nIElNRGLigJlzIEFQSSAoc2VlIFt3d3cub21kYmFwaS5jb21dKGh0dHA6Ly93d3cub21kYmFwaS5jb20vKSkgYW5kIGpvaW5pbmcgaXQgd2l0aCBhIHNlcGFyYXRlIGRhdGFzZXQgb2YgbW92aWUgYnVkZ2V0cyBhbmQgZ3Jvc3MgZWFybmluZ3MgKHVua25vd24gdG8geW91KS4gVGhlIGpvaW4ga2V5IHdhcyB0aGUgbW92aWUgdGl0bGUuIFRoaXMgZGF0YSBpcyBhdmFpbGFibGUgZm9yIHBlcnNvbmFsIHVzZSwgYnV0IElNRGLigJlzIHRlcm1zIG9mIHNlcnZpY2UgZG8gbm90IGFsbG93IGl0IHRvIGJlIHVzZWQgZm9yIGNvbW1lcmNpYWwgcHVycG9zZXMgb3IgZm9yIGNyZWF0aW5nIGEgY29tcGV0aW5nIHJlcG9zaXRvcnkuCgojIE9iamVjdGl2ZQoKWW91ciBnb2FsIGlzIHRvIGludmVzdGlnYXRlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgbW92aWUgZGVzY3JpcHRvcnMgYW5kIHRoZSBib3ggb2ZmaWNlIHN1Y2Nlc3Mgb2YgbW92aWVzLCBhcyByZXByZXNlbnRlZCBieSB0aGUgdmFyaWFibGUgYEdyb3NzYC4gVGhpcyB0YXNrIGlzIGV4dHJlbWVseSBpbXBvcnRhbnQgYXMgaXQgY2FuIGhlbHAgYSBzdHVkaW8gZGVjaWRlIHdoaWNoIHRpdGxlcyB0byBmdW5kIGZvciBwcm9kdWN0aW9uLCBob3cgbXVjaCB0byBiaWQgb24gcHJvZHVjZWQgbW92aWVzLCB3aGVuIHRvIHJlbGVhc2UgYSB0aXRsZSwgaG93IG11Y2ggdG8gaW52ZXN0IGluIG1hcmtldGluZyBhbmQgUFIsIGV0Yy4gVGhpcyBpbmZvcm1hdGlvbiBpcyBtb3N0IHVzZWZ1bCBiZWZvcmUgYSB0aXRsZSBpcyByZWxlYXNlZCwgYnV0IGl0IGlzIHN0aWxsIHZlcnkgdmFsdWFibGUgYWZ0ZXIgdGhlIG1vdmllIGlzIGFscmVhZHkgcmVsZWFzZWQgdG8gdGhlIHB1YmxpYyAoZm9yIGV4YW1wbGUgaXQgY2FuIGFmZmVjdCBhZGRpdGlvbmFsIG1hcmtldGluZyBzcGVuZCBvciBob3cgbXVjaCBhIHN0dWRpbyBzaG91bGQgbmVnb3RpYXRlIHdpdGggb24tZGVtYW5kIHN0cmVhbWluZyBjb21wYW5pZXMgZm9yIOKAnHNlY29uZCB3aW5kb3figJ0gc3RyZWFtaW5nIHJpZ2h0cykuCgojIEluc3RydWN0aW9ucwpUaGlzIGlzIGFuIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBOb3RlYm9vay4gT3BlbiB0aGlzIGZpbGUgaW4gUlN0dWRpbyB0byBnZXQgc3RhcnRlZC4KCldoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gVHJ5IGV4ZWN1dGluZyB0aGlzIGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqUnVuKiBidXR0b24gd2l0aGluIHRoZSBjaHVuayBvciBieSBwbGFjaW5nIHlvdXIgY3Vyc29yIGluc2lkZSBpdCBhbmQgcHJlc3NpbmcgKkNtZCtTaGlmdCtFbnRlciouIAoKYGBge3J9CnggPSAxOjEwCnByaW50KHheMikKYGBgCgpQbG90cyBhcHBlYXIgaW5saW5lIHRvbzoKYGBge3J9CnBsb3QoeCwgeF4yLCAnbycpCmBgYAoKQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkNtZCtPcHRpb24rSSouCgpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkNtZCtTaGlmdCtLKiB0byBwcmV2aWV3IHRoZSBIVE1MIGZpbGUpLgoKUGxlYXNlIGNvbXBsZXRlIHRoZSB0YXNrcyBiZWxvdyBhbmQgc3VibWl0IHRoaXMgUiBNYXJrZG93biBmaWxlIChhcyAqKnByMS5SbWQqKikgYXMgd2VsbCBhcyBhIFBERiBleHBvcnQgb2YgaXQgKGFzICoqcHIxLnBkZioqKS4gQm90aCBzaG91bGQgY29udGFpbiBhbGwgdGhlIGNvZGUsIG91dHB1dCwgcGxvdHMgYW5kIHdyaXR0ZW4gcmVzcG9uc2VzIGZvciBlYWNoIHRhc2suCgojIFNldHVwCgojIyBMb2FkIGRhdGEKCk1ha2Ugc3VyZSB5b3UndmUgZG93bmxvYWRlZCB0aGUgW2Btb3ZpZXNfbWVyZ2VkYF0oaHR0cHM6Ly9zMy5hbWF6b25hd3MuY29tL2NvbnRlbnQudWRhY2l0eS1kYXRhLmNvbS9jb3Vyc2VzL2d0LWNzNjI0Mi9wcm9qZWN0L21vdmllc19tZXJnZWQpIGZpbGUgYW5kIGl0IGlzIGluIHRoZSBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5LiBOb3cgbG9hZCBpdCBpbnRvIG1lbW9yeToKCmBgYHtyfQpsb2FkKCdtb3ZpZXNfbWVyZ2VkJykKYGBgCgpUaGlzIGNyZWF0ZXMgYW4gb2JqZWN0IG9mIHRoZSBzYW1lIG5hbWUgKGBtb3ZpZXNfbWVyZ2VkYCkuIEZvciBjb252ZW5pZW5jZSwgeW91IGNhbiBjb3B5IGl0IHRvIGBkZmAgYW5kIHN0YXJ0IHVzaW5nIGl0OgoKYGBge3J9CmRmID0gbW92aWVzX21lcmdlZApjYXQoIkRhdGFzZXQgaGFzIiwgZGltKGRmKVsxXSwgInJvd3MgYW5kIiwgZGltKGRmKVsyXSwgImNvbHVtbnMiLCBlbmQ9IlxuIiwgZmlsZT0iIikKY29sbmFtZXMoZGYpCmBgYAoKIyMgTG9hZCBSIHBhY2thZ2VzCgpMb2FkIGFueSBSIHBhY2thZ2VzIHRoYXQgeW91IHdpbGwgbmVlZCB0byB1c2UuIFlvdSBjYW4gY29tZSBiYWNrIHRvIHRoaXMgY2h1bmssIGVkaXQgaXQgYW5kIHJlLXJ1biB0byBsb2FkIGFueSBhZGRpdGlvbmFsIHBhY2thZ2VzIGxhdGVyLgoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShHR2FsbHkpCmxpYnJhcnkoc2NhbGVzKQpgYGAKCklmIHlvdSBhcmUgbG9hZGluZyBhbnkgbm9uLXN0YW5kYXJkIHBhY2thZ2VzIChvbmVzIHRoYXQgaGF2ZSBub3QgYmVlbiBkaXNjdXNzZWQgaW4gY2xhc3Mgb3IgZXhwbGljaXRseSBhbGxvd2VkIGZvciB0aGlzIHByb2plY3QpLCBwbGVhc2UgbWVudGlvbiB0aGVtIGJlbG93LiBJbmNsdWRlIGFueSBzcGVjaWFsIGluc3RydWN0aW9ucyBpZiB0aGV5IGNhbm5vdCBiZSBpbnN0YWxsZWQgdXNpbmcgdGhlIHJlZ3VsYXIgYGluc3RhbGwucGFja2FnZXMoJzxwa2cgbmFtZT4nKWAgY29tbWFuZC4KCioqTm9uLXN0YW5kYXJkIHBhY2thZ2VzIHVzZWQqKjogTm9uZQoKIyBUYXNrcwoKRWFjaCB0YXNrIGJlbG93IGlzIHdvcnRoICoqMTAqKiBwb2ludHMsIGFuZCBpcyBtZWFudCB0byBiZSBwZXJmb3JtZWQgc2VxdWVudGlhbGx5LCBpLmUuIGRvIHN0ZXAgMiBhZnRlciB5b3UgaGF2ZSBwcm9jZXNzZWQgdGhlIGRhdGEgYXMgZGVzY3JpYmVkIGluIHN0ZXAgMS4gVG90YWwgcG9pbnRzOiAqKjEwMCoqCgpDb21wbGV0ZSBlYWNoIHRhc2sgYnkgaW1wbGVtZW50aW5nIGNvZGUgY2h1bmtzIGFzIGRlc2NyaWJlZCBieSBgVE9ET2AgY29tbWVudHMsIGFuZCBieSByZXNwb25kaW5nIHRvIHF1ZXN0aW9ucyAoIioqUSoqOiIpIHdpdGggd3JpdHRlbiBhbnN3ZXJzICgiKipBKio6IikuIElmIHlvdSBhcmUgdW5hYmxlIHRvIGZpbmQgYSBtZWFuaW5nZnVsIG9yIHN0cm9uZyByZWxhdGlvbnNoaXAgaW4gYW55IG9mIHRoZSBjYXNlcyB3aGVuIHJlcXVlc3RlZCwgZXhwbGFpbiB3aHkgbm90IGJ5IHJlZmVycmluZyB0byBhcHByb3ByaWF0ZSBwbG90cy9zdGF0aXN0aWNzLgoKSXQgaXMgT0sgdG8gaGFuZGxlIG1pc3NpbmcgdmFsdWVzIGJlbG93IGJ5IG9taXNzaW9uLCBidXQgcGxlYXNlIG9taXQgYXMgbGl0dGxlIGFzIHBvc3NpYmxlLiBJdCBpcyB3b3J0aHdoaWxlIHRvIGludmVzdCBpbiByZXVzYWJsZSBhbmQgY2xlYXIgY29kZSBhcyB5b3UgbWF5IG5lZWQgdG8gdXNlIGl0IG9yIG1vZGlmeSBpdCBpbiBwcm9qZWN0IDIuCgojIyAxLiBSZW1vdmUgbm9uLW1vdmllIHJvd3MKClRoZSB2YXJpYWJsZSBgVHlwZWAgY2FwdHVyZXMgd2hldGhlciB0aGUgcm93IGlzIGEgbW92aWUsIGEgVFYgc2VyaWVzLCBvciBhIGdhbWUuIFJlbW92ZSBhbGwgcm93cyBmcm9tIGBkZmAgdGhhdCBkbyBub3QgY29ycmVzcG9uZCB0byBtb3ZpZXMuCgpgYGB7cn0KIyBUT0RPOiBSZW1vdmUgYWxsIHJvd3MgZnJvbSBkZiB0aGF0IGRvIG5vdCBjb3JyZXNwb25kIHRvIG1vdmllcwpkZiA8LSBzdWJzZXQoZGYsIFR5cGUgPT0gIm1vdmllIikKYGBgCgoqKlEqKjogSG93IG1hbnkgcm93cyBhcmUgbGVmdCBhZnRlciByZW1vdmFsPyBfRW50ZXIgeW91ciByZXNwb25zZSBiZWxvdy5fCgoqKkEqKjogNDAsMDAwCgojIyAyLiBQcm9jZXNzIGBSdW50aW1lYCBjb2x1bW4KClRoZSB2YXJpYWJsZSBgUnVudGltZWAgcmVwcmVzZW50cyB0aGUgbGVuZ3RoIG9mIHRoZSB0aXRsZSBhcyBhIHN0cmluZy4gV3JpdGUgUiBjb2RlIHRvIGNvbnZlcnQgaXQgdG8gYSBudW1lcmljIHZhbHVlIChpbiBtaW51dGVzKSBhbmQgcmVwbGFjZSBgZGYkUnVudGltZWAgd2l0aCB0aGUgbmV3IG51bWVyaWMgY29sdW1uLgoKYGBge3J9CiMgVE9ETzogUmVwbGFjZSBkZiRSdW50aW1lIHdpdGggYSBudW1lcmljIGNvbHVtbiBjb250YWluaW5nIHRoZSBydW50aW1lIGluIG1pbnV0ZXMKZGYkUnVudGltZSA9IGFzLm51bWVyaWMoZ3N1YignXFxEJywnJyxkZiRSdW50aW1lKSkKYGBgCgpOb3cgaW52ZXN0aWdhdGUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBgUnVudGltZWAgdmFsdWVzIGFuZCBob3cgaXQgY2hhbmdlcyBvdmVyIHllYXJzICh2YXJpYWJsZSBgWWVhcmAsIHdoaWNoIHlvdSBjYW4gYnVja2V0IGludG8gZGVjYWRlcykgYW5kIGluIHJlbGF0aW9uIHRvIHRoZSBidWRnZXQgKHZhcmlhYmxlIGBCdWRnZXRgKS4gSW5jbHVkZSBhbnkgcGxvdHMgdGhhdCBpbGx1c3RyYXRlLgoKYGBge3J9CiMgVE9ETzogSW52ZXN0aWdhdGUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBSdW50aW1lIHZhbHVlcyBhbmQgaG93IGl0IHZhcmllcyBieSBZZWFyIGFuZCBCdWRnZXQKZGYkRGVjYWRlID0gY3V0KGRmJFllYXIsIGJyZWFrcyA9IHNlcSgxODgwLDIwMjAsIGJ5ID0gMTApLCBsYWJlbHMgPSBwYXN0ZShhcy5jaGFyYWN0ZXIoc2VxKDE4ODAsIDIwMTAsIGJ5ID0gMTApKSwncycsc2VwID0gJycpKQojIERpc3RyaWJ1dGlvbiBvZiBSdW50aW1lCmdncGxvdChkZiwgYWVzKHggPSBSdW50aW1lKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzAsIGNvbG9yID0gJ2dvbGQnLCBmaWxsID0gJ2JsYWNrJykgKyAKICB4bGltKDAsMzAwKSArIGxhYnModGl0bGUgPSAnUnVudGltZSBIaXN0b2dyYW0nKSArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ2Rhcmt0dXJxdW9pc2UnKSkKYGBgCgpgYGB7cn0KIyBCdWRnZXQvUnVudGltZS9ZZWFyIFJlbGF0aW9uc2hpcCBQbG90CmdncGxvdChkZiwgYWVzKHggPSBCdWRnZXQsIHkgPSBSdW50aW1lLCBjb2xvciA9IERlY2FkZSkpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMSwgc3RhdCA9ICdzdW1tYXJ5JywgZnVuLnkgPSBtZWRpYW4pICsKICBsYWJzKHRpdGxlID0gJ1J1bnRpbWUgdnMgQnVkZ2V0IGJ5IERlY2FkZXMnKQpgYGAKCgoqKlEqKjogQ29tbWVudCBvbiB0aGUgZGlzdHJpYnV0aW9uIGFzIHdlbGwgYXMgcmVsYXRpb25zaGlwcy4gQXJlIHRoZXJlIGFueSBwYXR0ZXJucyBvciB0cmVuZHMgdGhhdCB5b3UgY2FuIG9ic2VydmU/CgoqKkEqKjogVGhlIGRpc3RyaWJ1dGlvbiBvZiBSdW50aW1lcyBpcyBhIHNvbWV3aGF0IG5vcm1hbCBkaXN0cmlidXRpb24gd2hlcmUgbW9zdCBvZiB0aGUgcnVudGltZXMgYXJlIHNsaWdodGx5IGxlc3MgdGhhbiAxMDAgbWludXRlcyBsb25nLiBBZnRlciBsb29raW5nIGF0IHRoZSBtb3JlIGluZGVwdGggcmVsYXRpb25zaGlwIGJldHdlZW4gUnVudGltZSwgQnVkZ2V0LCBhbmQgWWVhciAoYnkgRGVjYWRlKSwgdGhlIGZpcnN0IHRoaW5nIHRvIHBvaW50IG91dCB3YXMgdGhhdCBzZXZlcmFsIG9mIHRoZSBtb3ZpZXMgbWFkZSBwcmlvciB0byB0aGUgMTk3MHMgZGlkIG5vdCBoYXZlIEJ1ZGdldCBkYXRhIHN0b3JlZC4gV2l0aCB0aGlzIHRha2VuIGludG8gYWNjb3VudCwgYSBub3RpY2VhYmxlIHRyZW5kIHdhcyB0aGF0IHRoZSBydW50aW1lcyBmb3IgbW92aWVzIGZyb20gdGhlIDE5OTBzIHVudGlsIG5vdyBoYXZlIGdlbmVyYWxseSBzaG9ydGVyIHJ1bnRpbWVzLiBBbHNvLCB0aGVzZSBtb3ZpZXMgaGF2ZSBhIG11Y2ggd2lkZXIgcmFuZ2UgaW4gQnVkZ2V0LiBPbiB0aGUgb3RoZXIgaGFuZCwgemVybyBtb3ZpZXMgbWFkZSBiZWZvcmUgdGhlIDE5OTBzIGhhZCBhIGJ1ZGdldCBleGNlZWRpbmcgJDgwLDAwMCwwMDAuCgojIyAzLiBFbmNvZGUgYEdlbnJlYCBjb2x1bW4KClRoZSBjb2x1bW4gYEdlbnJlYCByZXByZXNlbnRzIGEgbGlzdCBvZiBnZW5yZXMgYXNzb2NpYXRlZCB3aXRoIHRoZSBtb3ZpZSBpbiBhIHN0cmluZyBmb3JtYXQuIFdyaXRlIGNvZGUgdG8gcGFyc2UgZWFjaCB0ZXh0IHN0cmluZyBpbnRvIGEgYmluYXJ5IHZlY3RvciB3aXRoIDFzIHJlcHJlc2VudGluZyB0aGUgcHJlc2VuY2Ugb2YgYSBnZW5yZSBhbmQgMHMgdGhlIGFic2VuY2UsIGFuZCBhZGQgaXQgdG8gdGhlIGRhdGFmcmFtZSBhcyBhZGRpdGlvbmFsIGNvbHVtbnMuIFRoZW4gcmVtb3ZlIHRoZSBvcmlnaW5hbCBgR2VucmVgIGNvbHVtbi4KCkZvciBleGFtcGxlLCBpZiB0aGVyZSBhcmUgYSB0b3RhbCBvZiAzIGdlbnJlczogRHJhbWEsIENvbWVkeSwgYW5kIEFjdGlvbiwgYSBtb3ZpZSB0aGF0IGlzIGJvdGggQWN0aW9uIGFuZCBDb21lZHkgc2hvdWxkIGJlIHJlcHJlc2VudGVkIGJ5IGEgYmluYXJ5IHZlY3RvciA8MCwgMSwgMT4uIE5vdGUgdGhhdCB5b3UgbmVlZCB0byBmaXJzdCBjb21waWxlIGEgZGljdGlvbmFyeSBvZiBhbGwgcG9zc2libGUgZ2VucmVzIGFuZCB0aGVuIGZpZ3VyZSBvdXQgd2hpY2ggbW92aWUgaGFzIHdoaWNoIGdlbnJlcyAoeW91IGNhbiB1c2UgdGhlIFIgYHRtYCBwYWNrYWdlIHRvIGNyZWF0ZSB0aGUgZGljdGlvbmFyeSkuCgpgYGB7cn0KIyBUT0RPOiBSZXBsYWNlIEdlbnJlIHdpdGggYSBjb2xsZWN0aW9uIG9mIGJpbmFyeSBjb2x1bW5zCnVuaXFfZ2VucmVfZ3JvdXBzID0gdW5pcXVlKGRmJEdlbnJlKQppbmRpR2VucmVzID0gcmVwKDAsIHRpbWVzID0gMCkKZm9yKGkgaW4gc2VxKDEsbGVuZ3RoKHVuaXFfZ2VucmVfZ3JvdXBzKSkpewogIGluZGlHZW5yZXMgPSBhcHBlbmQoaW5kaUdlbnJlcywgdW5saXN0KHN0cnNwbGl0KHVuaXFfZ2VucmVfZ3JvdXBzW2ldLCBzcGxpdCA9ICcsICcpKSkKfQppbmRpR2VucmVzID0gc29ydCh1bmlxdWUoaW5kaUdlbnJlcykpCmluaXRCaW5HZW5yZVZlY3RvciA9IHJlcCgwLCB0aW1lcyA9IGxlbmd0aChpbmRpR2VucmVzKSkKR2VucmUgPSBsaXN0KCkKZm9yKGkgaW4gc2VxKDEsZGltKGRmKVsxXSkpewogIEdlbnJlW1tpXV0gPSBpbml0QmluR2VucmVWZWN0b3IKICBmb3IoaiBpbiBzZXEoMSxsZW5ndGgoaW5kaUdlbnJlcykpKXsKICAgIGlmKGdyZXBsKGluZGlHZW5yZXNbal0sIGRmJEdlbnJlW2ldKSl7CiAgICAgIEdlbnJlW1tpXV1bal0gPSAxCiAgICB9CiAgfQp9CmRmJEdlbnJlID0gR2VucmUKcm0odW5pcV9nZW5yZV9ncm91cHMsR2VucmUsIGksIGopCmBgYAoKUGxvdCB0aGUgcmVsYXRpdmUgcHJvcG9ydGlvbnMgb2YgbW92aWVzIGhhdmluZyB0aGUgdG9wIDEwIG1vc3QgY29tbW9uIGdlbnJlcy4KCmBgYHtyfQojIFRPRE86IFNlbGVjdCBtb3ZpZXMgZnJvbSB0b3AgMTAgbW9zdCBjb21tb24gZ2VucmVzIGFuZCBwbG90IHRoZWlyIHJlbGF0aXZlIHByb3BvcnRpb25zCmdlbnJlX2NudHMgPSBpbml0QmluR2VucmVWZWN0b3IKZm9yKGkgaW4gc2VxKDEsZGltKGRmKVsxXSkpewogIGdlbnJlX2NudHMgPSBnZW5yZV9jbnRzICsgZGYkR2VucmVbW2ldXQp9CnJhbmtlZEdlbnJlcyA9IHJhbmsoLWdlbnJlX2NudHMpCkdlbnJlcyA9IGRhdGEuZnJhbWUoR2VucmUgPSBpbmRpR2VucmVzLCBSYW5rID0gcmFua2VkR2VucmVzLCBDb3VudCA9IGdlbnJlX2NudHMpCkdlbnJlcyRNb3ZpZVBlcmNlbnRhZ2UgPSByb3VuZChHZW5yZXMkQ291bnQvZGltKGRmKVsxXSwgMykqMTAwCnJtKHJhbmtlZEdlbnJlcywgZ2VucmVfY250cywgaW5kaUdlbnJlcywgaSkKZ2dwbG90KGRhdGEgPSBHZW5yZXNbR2VucmVzJFJhbmsgPD0gMTAsXSwgYWVzKHggPSBHZW5yZSwgeSA9IE1vdmllUGVyY2VudGFnZSwgY29sb3IgPSBHZW5yZSkpICsKICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBNb3ZpZVBlcmNlbnRhZ2UpLCB2anVzdCA9IDEuNSkgKwogICN5bGltKDAsMTAwKSArCiAgbGFicyh0aXRsZSA9ICdUb3AgMTAgTW9zdCBDb21tb24gR2VucmVzJywgeCA9ICdHZW5yZScsIHkgPSAnUGVyY2VudGFnZSAoJSkgb2YgVG90YWwgTW92aWVzIHdpdGggR2VucmUnKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpCgpgYGAKCkV4YW1pbmUgaG93IHRoZSBkaXN0cmlidXRpb24gb2YgYFJ1bnRpbWVgIGNoYW5nZXMgYWNyb3NzIGdlbnJlcyBmb3IgdGhlIHRvcCAxMCBtb3N0IGNvbW1vbiBnZW5yZXMuCgpgYGB7cn0KIyBUT0RPOiBQbG90IFJ1bnRpbWUgZGlzdHJpYnV0aW9uIGZvciB0b3AgMTAgbW9zdCBjb21tb24gZ2VucmVzCk4gPSAyMDAwMDAKeCA9IDAKcnVuVGltZVRvcEdlbnJlID0gZGF0YS5mcmFtZShHZW5yZSA9IHJlcCgnJywgTiksIFJ1bnRpbWUgPSByZXAoTkEsIE4pKQpydW5UaW1lVG9wR2VucmUkR2VucmUgPSBhcy5jaGFyYWN0ZXIocnVuVGltZVRvcEdlbnJlJEdlbnJlKQpHZW5yZXMkR2VucmUgPSBhcy5jaGFyYWN0ZXIoR2VucmVzJEdlbnJlKQojIFJvdWdobHkgOCBtaW4gcnVudGltZQpmb3IoaSBpbiBzZXEoMSxkaW0oZGYpWzFdKSl7CiAgZm9yKGogaW4gc2VxKDEsZGltKEdlbnJlcylbMV0pKXsKICAgIGlmKGRmJEdlbnJlW1tpXV1bal0gPT0gMSAmIGogJWluJSByb3cubmFtZXMoR2VucmVzW0dlbnJlcyRSYW5rIDw9IDEwLCBdKSl7CiAgICAgIHggPSB4ICsgMQogICAgICBydW5UaW1lVG9wR2VucmUkR2VucmVbeF0gPSBHZW5yZXMkR2VucmVbal0KICAgICAgcnVuVGltZVRvcEdlbnJlJFJ1bnRpbWVbeF0gPSBkZiRSdW50aW1lW2ldCiAgICB9CiAgfQp9CnJ1blRpbWVUb3BHZW5yZSA9IHJ1blRpbWVUb3BHZW5yZVshYXBwbHkoaXMubmEocnVuVGltZVRvcEdlbnJlKSB8IHJ1blRpbWVUb3BHZW5yZSA9PSAnJywgMSwgYWxsKSxdCnJtKGksIGosIE4sIHgpCmRmXzMgPSBkZgpnZ3Bsb3QocnVuVGltZVRvcEdlbnJlLCBhZXMoeCA9IEdlbnJlLCB5ID0gUnVudGltZSwgZmlsbCA9IEdlbnJlKSkgKwogIGdlb21fYm94cGxvdCgpICsKICB5bGltKDAsMzUwKSArCiAgbGFicyh0aXRsZSA9ICdUb3AgMTAgR2VucmVzOiBSdW50aW1lIERpc3RyaWJ1dGlvbiBCb3ggUGxvdHMnLCB4ID0gJ0dlbnJlJywgeSA9ICdSdW50aW1lIChtaW4pJykgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCioqUSoqOiBEZXNjcmliZSB0aGUgaW50ZXJlc3RpbmcgcmVsYXRpb25zaGlwKHMpIHlvdSBvYnNlcnZlLiBBcmUgdGhlcmUgYW55IGV4cGVjdGVkIG9yIHVuZXhwZWN0ZWQgdHJlbmRzIHRoYXQgYXJlIGV2aWRlbnQ/CgoqKkEqKjogJ0RyYW1hJyBnZW5yZXMgd2VyZSB0aGUgbW9zdCBwb3B1bGFyLCByZXByZXNlbnRpbmcgYWxtb3N0IDQwJSBvZiB0aGUgbW92aWVzLiBJIGZvdW5kIHRoYXQgJ1RocmlsbGVyJyBhbmQgJ0FuaW1hdGlvbicgZmlsbXMgKHJlcHJlc2VudGluZyA4LjQlIGFuZCA3JSBvZiB0aGUgbW92aWVzIHJlc3BlY3RpdmVseSkgd2VyZSBub3RpY2VhYmx5IHNob3J0ZXIgdGhhbiB0aGUgcmVzdCBvZiB0aGUgdG9wIDEwIEdlbnJlcy4gIEFsc28sICdBbmltYXRpb24nIGZpbG1zIGhhZCBzdWNoIGEgc21hbGwgaW50ZXJxdWFydGlsZSByYW5nZSAobmFycm93IGNvbmNlbnRyYXRlZCBkaXN0cmlidXRpb24pIHRoYXQgeW91IGNhbiBiYXJlbHkgc2VlIHRoZSBib3guICAnRG9jdW1lbnRhcnknIGZpbG1zIChyZXByZXNlbnRpbmcgNy42JSBvZiB0aGUgbW92aWVzKSBoYWQgdGhlIGxhcmdlc3QgaW50ZXJxdWFydGlsZSByYW5nZSBtZWFuaW5nIHRoYXQgaXQgaGFkIHRoZSB3aWRlc3QgY29uY2VudHJhdGVkIGRpc3RyaWJ1dGlvbi4gVGhlcmUgYXJlIGFsc28gcGxlbnR5IG9mIG91dGxpZXJzLiBJIGN1dCB0aGUgUnVudGltZSByYW5nZSB0byBsZXNzIHRoYW4gMzUwIG1pbnV0ZXMsIGJ1dCB0aGVyZSB3ZXJlIHNldmVyYWwgb2Ygc3RyYWdnbGVycyBvdXRzaWRlIHRoYXQgcmFuZ2UuCgojIyA0LiBFbGltaW5hdGUgbWlzbWF0Y2hlZCByb3dzCgpUaGUgZGF0YWZyYW1lIHdhcyBwdXQgdG9nZXRoZXIgYnkgbWVyZ2luZyB0d28gZGlmZmVyZW50IHNvdXJjZXMgb2YgZGF0YSBhbmQgaXQgaXMgcG9zc2libGUgdGhhdCB0aGUgbWVyZ2luZyBwcm9jZXNzIHdhcyBpbmFjY3VyYXRlIGluIHNvbWUgY2FzZXMgKHRoZSBtZXJnZSB3YXMgZG9uZSBiYXNlZCBvbiBtb3ZpZSB0aXRsZSwgYnV0IHRoZXJlIGFyZSBjYXNlcyBvZiBkaWZmZXJlbnQgbW92aWVzIHdpdGggdGhlIHNhbWUgdGl0bGUpLiBUaGUgZmlyc3Qgc291cmNl4oCZcyByZWxlYXNlIHRpbWUgd2FzIHJlcHJlc2VudGVkIGJ5IHRoZSBjb2x1bW4gYFllYXJgIChudW1lcmljIHJlcHJlc2VudGF0aW9uIG9mIHRoZSB5ZWFyKSBhbmQgdGhlIHNlY29uZCBieSB0aGUgY29sdW1uIGBSZWxlYXNlZGAgKHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiByZWxlYXNlIGRhdGUpLgoKRmluZCBhbmQgcmVtb3ZlIGFsbCByb3dzIHdoZXJlIHlvdSBzdXNwZWN0IGEgbWVyZ2UgZXJyb3Igb2NjdXJyZWQgYmFzZWQgb24gYSBtaXNtYXRjaCBiZXR3ZWVuIHRoZXNlIHR3byB2YXJpYWJsZXMuIFRvIG1ha2Ugc3VyZSBzdWJzZXF1ZW50IGFuYWx5c2lzIGFuZCBtb2RlbGluZyB3b3JrIHdlbGwsIGF2b2lkIHJlbW92aW5nIG1vcmUgdGhhbiAxMCUgb2YgdGhlIHJvd3MgdGhhdCBoYXZlIGEgYEdyb3NzYCB2YWx1ZSBwcmVzZW50LgoKYGBge3J9CiMgVE9ETzogUmVtb3ZlIHJvd3Mgd2l0aCBSZWxlYXNlZC1ZZWFyIG1pc21hdGNoCiMgUmVtb3ZlIGFsbCBtaXNtYXRjaGVzIHRoYXQgZG8gbm90IGhhdmVhICBHcm9zcyB2YWx1ZXMKbWlzbWF0Y2hOQWdyb3NzX2luZCA9ICghaXMubmEoZGYkUmVsZWFzZWQpICYgaXMubmEoZGYkR3Jvc3MpICYgYWJzKGFzLm51bWVyaWMoZm9ybWF0KGRmJFJlbGVhc2VkLCAnJVknKSkgLSBkZiRZZWFyKSA+IDApCmRmMiA9IGRmWyFtaXNtYXRjaE5BZ3Jvc3NfaW5kLF0KcHJpbnQocGFzdGUoJ051bWJlciBvZiBSb3dzIHJlbW92ZWQgd2l0aG91dCBHcm9zcyBWYWx1ZXMgPScsIGRpbShkZilbMV0gLSBkaW0oZGYyKVsxXSkpCm51bVdpdGhHcm9zcyA9IHN1bSghaXMubmEoZGYkR3Jvc3MpKQp0ZW5QZXJHcm9zcyA9IG51bVdpdGhHcm9zcy8xMApkaWZmID0gLTEKTlVNID0gdGVuUGVyR3Jvc3MgKyAxCndoaWxlKE5VTSA+IHRlblBlckdyb3NzKXsKICBkaWZmID0gZGlmZiArIDEKICBOVU0gPSBzdW0oIWlzLm5hKGRmMiRSZWxlYXNlZCkgJiAhaXMubmEoZGYyJEdyb3NzKSAmIGFicyhhcy5udW1lcmljKGZvcm1hdChkZjIkUmVsZWFzZWQsICclWScpKSAtIGRmMiRZZWFyKSA+IGRpZmYpCiAgbWlzbWF0Y2hXZ3Jvc3NfaW5kID0gKCFpcy5uYShkZjIkUmVsZWFzZWQpICYgIWlzLm5hKGRmMiRHcm9zcykgJiBhYnMoYXMubnVtZXJpYyhmb3JtYXQoZGYyJFJlbGVhc2VkLCAnJVknKSkgLSBkZjIkWWVhcikgPiBkaWZmKQp9CmRmMyA9IGRmMlshbWlzbWF0Y2hXZ3Jvc3NfaW5kLF0KcHJpbnQocGFzdGUocm91bmQoTlVNL251bVdpdGhHcm9zcyoxMDAsIDIpLCAnJSBvZiBHcm9zcyB2YWx1ZWQgcmVjb3JkcyByZW1vdmVkJywgc2VwID0gJycpKQpwcmludChwYXN0ZSgnTnVtYmVyIG9mIFJvd3MgcmVtb3ZlZCB3aXRoIEdyb3NzIFZhbHVlcyA9JywgZGltKGRmMilbMV0gLSBkaW0oZGYzKVsxXSkpCnByaW50KHBhc3RlKCdUb3RhbCBudW1iZXIgb2Ygcm93cyByZW1vdmVkID0nLCBkaW0oZGYpWzFdIC0gZGltKGRmMylbMV0pKQpkZiA9IGRmMwpybShkaWZmLCBkZjIsIGRmMywgbWlzbWF0Y2hOQWdyb3NzX2luZCwgbWlzbWF0Y2hXZ3Jvc3NfaW5kLCBOVU0sIG51bVdpdGhHcm9zcywgdGVuUGVyR3Jvc3MpCmRmXzQgPSBkZgoKYGBgCgoqKlEqKjogV2hhdCBpcyB5b3VyIHByZWNpc2UgcmVtb3ZhbCBsb2dpYyBhbmQgaG93IG1hbnkgcm93cyBkaWQgeW91IGVuZCB1cCByZW1vdmluZz8KCioqQSoqOiBUaGUgZmlyc3QgdGhpbmcgSSBkaWQgd2FzIGlnbm9yZSBhbnkgTkEgdmFsdWVzIGluIHRoZSAnUmVsZWFzZWQnIGNvbHVtbi4gIE5leHQgdGhpbmcgSSBkaWQgd2FzIHJlbW92ZSBhbGwgcm93cyB3aGljaCAnR3Jvc3MnIHdhcyBtaXNzaW5nIHZhbHVlcyAoTkEpIGFuZCB0aGUgeWVhcnMgZGlkIG5vdCBtYXRjaCB1cCBiZXR3ZWVuICdZZWFyJyBhbmQgJ1JlbGVhc2VkJy4gIFRoZSBuZXh0IHN0ZXAgd2FzIHRvIHJlbW92ZSB0aGUgcm93cyB0aGF0IGRpZCBub3QgbWF0Y2ggdXAgYmV0d2VlbiAnWWVhcicgYW5kICdSZWxlYXNlZCcgd2l0aG91dCBzdXJwYXNpbmcgMTAlIG9mIHRoZSByZWNvcmRzIHdpdGggR3Jvc3MgdmFsdWVzLiAgVG8gZG8gdGhpcywgSSBjaGVja2VkIHRoZSBkaWZmZXJlbmNlIGluIHllYXJzIGJldHdlZW4gJ1llYXInIGFuZCAnUmVsZWFzZWQnIGluY3JpbWVudGFsbHkgd2hpbGUga2VlcGluZyB0cmFjayBvZiBob3cgbWFueSByZWNvcmRzIHdvdWxkIGJlIHJlbW92ZWQuIEl0IGl0ZXJhdGVzIChpbmNyZWFzaW5nIHRoZSBkaWZmZXJlbmNlIHRocmVzaG9sZCB2YWx1ZSBlYWNoIHRpbWUpIHVudGlsIHRoZSBzdW0gb2YgdGhlIG51bWJlciBvZiByb3dzIHRoYXQgd2VyZSB0byBiZSByZW1vdmVkIGlzIGxlc3MgdGhhbiAxMCUgb2YgdGhlIHJvd3Mgd2l0aCBHcm9zcyB2YWx1ZXMuIFRoaXMgcHJvY2VzcyBlbmRzIHVwIHJlbW92aW5nIHJvd3MgdGhhdCBoYXZlICdZZWFyJyBhbmQgJ1JlbGVhc2VkJyBkaWZmZXJlbmNlcyBncmVhdGVyIHRoYW4gMSAoRXg6IGRpZmYoMjAwNCwyMDA2KSA+IDEpLiAgQWZ0ZXIgYWxsIG9mIHRoaXMgaXMgc2FpZCBhbmQgZG9uZSwgNTA0OCByb3dzIHdlcmUgcmVtb3ZlZCBpbiB0aGUgcHJvY2Vzcy4KCiMjIDUuIEV4cGxvcmUgYEdyb3NzYCByZXZlbnVlCgpGb3IgdGhlIGNvbW1lcmNpYWwgc3VjY2VzcyBvZiBhIG1vdmllLCBwcm9kdWN0aW9uIGhvdXNlcyB3YW50IHRvIG1heGltaXplIEdyb3NzIHJldmVudWUuIEludmVzdGlnYXRlIGlmIEdyb3NzIHJldmVudWUgaXMgcmVsYXRlZCB0byBCdWRnZXQsIFJ1bnRpbWUgb3IgR2VucmUgaW4gYW55IHdheS4KCk5vdGU6IFRvIGdldCBhIG1lYW5pbmdmdWwgcmVsYXRpb25zaGlwLCB5b3UgbWF5IGhhdmUgdG8gcGFydGl0aW9uIHRoZSBtb3ZpZXMgaW50byBzdWJzZXRzIHN1Y2ggYXMgc2hvcnQgdnMuIGxvbmcgZHVyYXRpb24sIG9yIGJ5IGdlbnJlLCBldGMuCgpgYGB7cn0KIyBUT0RPOiBJbnZlc3RpZ2F0ZSBpZiBHcm9zcyBSZXZlbnVlIGlzIHJlbGF0ZWQgdG8gQnVkZ2V0LCBSdW50aW1lIG9yIEdlbnJlCmRmXzUgPSBkZlshaXMubmEoZGYkR3Jvc3MpLF0KZGZfNSRNb250aFJlbGVhc2VkID0gYXMubnVtZXJpYyhmb3JtYXQoZGZfNSRSZWxlYXNlZCwgJyVtJykpCk4gPSAyMDAwMAp4ID0gMApncm9zcyA9IGRhdGEuZnJhbWUoR2VucmUgPSByZXAoJycsIE4pLCBSdW50aW1lID0gcmVwKE5BLCBOKSwgQnVkZ2V0ID0gcmVwKE5BLCBOKSwgR3Jvc3MgPSByZXAoTkEsIE4pKQpncm9zcyRHZW5yZSA9IGFzLmNoYXJhY3Rlcihncm9zcyRHZW5yZSkKIyBSb3VnaGx5IDIgbWluIHJ1bnRpbWUKZm9yKGkgaW4gc2VxKDEsZGltKGRmXzUpWzFdKSl7CiAgZm9yKGogaW4gc2VxKDEsZGltKEdlbnJlcylbMV0pKXsKICAgIGlmKGRmXzUkR2VucmVbW2ldXVtqXSA9PSAxICYgaiAlaW4lIHJvdy5uYW1lcyhHZW5yZXNbR2VucmVzJFJhbmsgPD0gMTEsIF0pKXsKICAgICAgeCA9IHggKyAxCiAgICAgIGdyb3NzJEdlbnJlW3hdID0gR2VucmVzJEdlbnJlW2pdCiAgICAgIGdyb3NzJFJ1bnRpbWVbeF0gPSBkZl81JFJ1bnRpbWVbaV0KICAgICAgZ3Jvc3MkQnVkZ2V0W3hdID0gZGZfNSRCdWRnZXRbaV0KICAgICAgZ3Jvc3MkR3Jvc3NbeF0gPSBkZl81JEdyb3NzW2ldCiAgICAgIGdyb3NzJE1vbnRoUmVsZWFzZWRbeF0gPSBkZl81JE1vbnRoUmVsZWFzZWRbaV0KICAgIH0KICB9Cn0KZ3Jvc3MgPSBncm9zc1shYXBwbHkoaXMubmEoZ3Jvc3MpIHwgZ3Jvc3MgPT0gJycsIDEsIGFsbCksXQpybShpLCBqLCBOLCB4KQpncm9zcyRSdW5UaW1lID0gY3V0KGdyb3NzJFJ1bnRpbWUsIGJyZWFrcyA9IHNlcSgwLDIyMCwgYnkgPSAyMCksIAogICAgICAgICAgICAgICAgbGFiZWxzID0gcGFzdGUoYXMuY2hhcmFjdGVyKHNlcSgwLCAyMDAsIGJ5ID0gMjApKSwnbWluJykpCmBgYAoKYGBge3J9CgpnZ3Bsb3QoZ3Jvc3MsIGFlcyh4ID0gQnVkZ2V0LCB5ID0gR3Jvc3MpKSArIAogIGZhY2V0X3dyYXAofiBHZW5yZSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNykgKwogIGdlb21fc21vb3RoKCkgKwogIHhsaW0oMCwyZTgpICsgeWxpbSgwLDEuNWU5KQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZ3Jvc3MsIGFlcyh4ID0gQnVkZ2V0LCB5ID0gR3Jvc3MsIGNvbG9yID0gUnVuVGltZSkpICsgCiAgZmFjZXRfd3JhcCh+IEdlbnJlKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC43KSArCiAgeGxpbSgwLDJlOCkgKyB5bGltKDAsMS41ZTkpCmBgYAoKYGBge3J9CmdncGxvdChncm9zcywgYWVzKHggPSBHZW5yZSwgeSA9IEdyb3NzLCBmaWxsID0gR2VucmUpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIHlsaW0oMCwxLjVlOSkgKwogIGxhYnModGl0bGUgPSAnR3Jvc3MgRGlzdHJpYnV0aW9uIEJveCBQbG90cyBieSBHZW5yZScsIHggPSAnR2VucmUnLCB5ID0gJ0dyb3NzICgkKScpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpgYGB7cn0KZ2dwbG90KGdyb3NzLCBhZXMoeCA9IFJ1blRpbWUsIHkgPSBHcm9zcywgZmlsbCA9IFJ1blRpbWUpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIHlsaW0oMCwxLjVlOSkgKwogIGxhYnModGl0bGUgPSAnR3Jvc3MgRGlzdHJpYnV0aW9uIEJveCBQbG90cyBieSBSdW5UaW1lJywgeCA9ICdSdW5UaW1lJywgeSA9ICdHcm9zcyAoJCknKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKCioqUSoqOiBEaWQgeW91IGZpbmQgYW55IG9ic2VydmFibGUgcmVsYXRpb25zaGlwcyBvciBjb21iaW5hdGlvbnMgb2YgQnVkZ2V0L1J1bnRpbWUvR2VucmUgdGhhdCByZXN1bHQgaW4gaGlnaCBHcm9zcyByZXZlbnVlPyBJZiB5b3UgZGl2aWRlZCB0aGUgbW92aWVzIGludG8gZGlmZmVyZW50IHN1YnNldHMsIHlvdSBtYXkgZ2V0IGRpZmZlcmVudCBhbnN3ZXJzIGZvciB0aGVtIC0gcG9pbnQgb3V0IGludGVyZXN0aW5nIG9uZXMuCgoqKkEqKjogVGhlIHJlc3VsdHMgSSBmb3VuZCBmcm9tIHRoZSBhYm92ZSBncmFwaHMgd2VyZSBpbnRlcmVzdGluZy4gSXQgc2VlbXMgZm9yIGFsbCBHZW5yZXMsIHRoZXJlIGlzIGEgcG9zaXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiBpbmNyZWFzaW5nIHRoZSBCdWRnZXQgYW5kIGluY3JlYXNlZCBHcm9zcyByZXZlbnVlLiAgRnJvbSB0aGUgbWlkZGxlIHR3byBwbG90cywgeW91IGNhbiBzZWUgdGhhdCBBbmltYXRpb24gYW5kIEFkdmVudHVyZSBHZW5yZXMgaGlzdG9yaWNhbGx5IGhhcyBwb3RlbnRpYWwgdG8gcHJvZHVjZSBoaWdoZXIgZ3Jvc3MgcmV2ZW51ZXMgdGhhbiB0aGUgcmVzdCBvZiB0aGUgZ2VucmVzLiAgQWxzbywgb2JzZXJ2aW5nIHJ1bnRpbWVzIGluIHRoZSBzZWNvbmQgYW5kIGxhc3QgcGxvdCwgeW91IGNhbiBzZWUgYSBzb3J0IG9mIGJlbGwgY3VydmUgd2l0aCBwZWFrIGdyb3NzIHJldmVudWVzIGFyb3VuZCAxNDAgbWluIHRvIDE2MCBtaW4uCgpgYGB7cn0KIyBUT0RPOiBJbnZlc3RpZ2F0ZSBpZiBHcm9zcyBSZXZlbnVlIGlzIHJlbGF0ZWQgdG8gUmVsZWFzZSBNb250aApnZ3Bsb3QoZ3Jvc3MsIGFlcyh4ID0gYXMuZmFjdG9yKE1vbnRoUmVsZWFzZWQpLCB5ID0gR3Jvc3MsIGNvbG9yID0gTW9udGhSZWxlYXNlZCkpICsKICBnZW9tX2JveHBsb3QoKSArCiAgeWxpbSgwLDEuNWU5KSArCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50KGxvdyA9ICdyZWQnLCBoaWdoID0gJ2JsdWUnKSArCiAgbGFicyh0aXRsZSA9ICdHcm9zcyBEaXN0cmlidXRpb24gQm94IFBsb3RzIGJ5IFJlbGVhc2UgTW9udGgnLCB4ID0gJ1JlbGVhc2UgTW9udGgnLCB5ID0gJ0dyb3NzICgkKScpCmBgYAoKKipBKio6IEl0IHNlZW1zIGxpa2UgdGhlIGhpZ2hlc3QgZ3Jvc3NpbmcgbW92aWVzIGFyZSByZWxlYXNlZCBpbiBlYXJseSBzdW1tZXIgd2l0aCBhIG5vdGViYWxlIG1lbnRpb24gZHVyaW5nIHRoZSBob2xpZGF5cyAobGFzdCB0d28gbW9udGhzIG9mIHRoZSB5ZWFyKS4KCiMjIDYuIFByb2Nlc3MgYEF3YXJkc2AgY29sdW1uCgpUaGUgdmFyaWFibGUgYEF3YXJkc2AgZGVzY3JpYmVzIG5vbWluYXRpb25zIGFuZCBhd2FyZHMgaW4gdGV4dCBmb3JtYXQuIENvbnZlcnQgaXQgdG8gMiBudW1lcmljIGNvbHVtbnMsIHRoZSBmaXJzdCBjYXB0dXJpbmcgdGhlIG51bWJlciBvZiB3aW5zLCBhbmQgdGhlIHNlY29uZCBjYXB0dXJpbmcgbm9taW5hdGlvbnMuIFJlcGxhY2UgdGhlIGBBd2FyZHNgIGNvbHVtbiB3aXRoIHRoZXNlIG5ldyBjb2x1bW5zLCBhbmQgdGhlbiBzdHVkeSB0aGUgcmVsYXRpb25zaGlwIG9mIGBHcm9zc2AgcmV2ZW51ZSB3aXRoIHJlc3BlY3QgdG8gdGhlbS4KCk5vdGUgdGhhdCB0aGUgZm9ybWF0IG9mIHRoZSBgQXdhcmRzYCBjb2x1bW4gaXMgbm90IHN0YW5kYXJkOyB5b3UgbWF5IGhhdmUgdG8gdXNlIHJlZ3VsYXIgZXhwcmVzc2lvbnMgdG8gZmluZCB0aGUgcmVsZXZhbnQgdmFsdWVzLiBUcnkgeW91ciBiZXN0IHRvIHByb2Nlc3MgdGhlbSwgYW5kIHlvdSBtYXkgbGVhdmUgdGhlIG9uZXMgdGhhdCBkb24ndCBoYXZlIGVub3VnaCBpbmZvcm1hdGlvbiBhcyBOQXMgb3Igc2V0IHRoZW0gdG8gMHMuCgpgYGB7cn0KIyBUT0RPOiBDb252ZXJ0IEF3YXJkcyB0byAyIG51bWVyaWMgY29sdW1uczogd2lucyBhbmQgbm9taW5hdGlvbnMKZGZfNiA9IGRmCmRmXzYkV2lucyA9IHJlcCgwLCB0aW1lcyA9IGRpbShkZl82KVsxXSkKZGZfNiROb21pbmF0aW9ucyA9IHJlcCgwLCB0aW1lcyA9IGRpbShkZl82KVsxXSkKZm9yKGkgaW4gc2VxKDE6ZGltKGRmXzYpWzFdKSl7CiAgd2lucyA9IDAKICBub21pbmF0aW9ucyA9IDAKICBpZihncmVwbCgiV29uICIsIGRmXzYkQXdhcmRzW2ldKSl7CiAgICB3aW5zID0gd2lucyArIGFzLm51bWVyaWMoZ3N1YigiLipXb24oXFxzKVxcYihcXGQrKS4qIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJcXDIiLCBkZl82JEF3YXJkc1tpXSwgcGVybD1UUlVFKSkKICB9CiAgaWYoZ3JlcGwoIiB3aW4iLCBkZl82JEF3YXJkc1tpXSkpewogICAgd2lucyA9IHdpbnMgKyBhcy5udW1lcmljKGdzdWIoIi4qXFxiKFxcZCspKFxccyl3aW4uKiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiXFwxIiwgZGZfNiRBd2FyZHNbaV0sIHBlcmw9VFJVRSkpCiAgfQogIGlmKGdyZXBsKCJOb21pbmF0ZWQgZm9yICIsIGRmXzYkQXdhcmRzW2ldKSl7CiAgICBub21pbmF0aW9ucyA9IG5vbWluYXRpb25zICsgYXMubnVtZXJpYyhnc3ViKCIuKk5vbWluYXRlZCBmb3IoXFxzKVxcYihcXGQrKS4qIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlxcMiIsIGRmXzYkQXdhcmRzW2ldLCBwZXJsPVRSVUUpKQogIH0KICBpZihncmVwbCgiIG5vbWluYXRpb24iLCBkZl82JEF3YXJkc1tpXSkpewogICAgbm9taW5hdGlvbnMgPSBub21pbmF0aW9ucyArIGFzLm51bWVyaWMoZ3N1YigiLipcXGIoXFxkKykoXFxzKW5vbWluYXRpb24uKiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJcXDEiLCBkZl82JEF3YXJkc1tpXSwgcGVybD1UUlVFKSkKICB9CiAgZGZfNiRXaW5zW2ldID0gd2lucwogIGRmXzYkTm9taW5hdGlvbnNbaV0gPSBub21pbmF0aW9ucwp9CmRmXzYkQXdhcmRzIDwtIE5VTEwKcm0oaSx3aW5zLG5vbWluYXRpb25zKQpgYGAKCmBgYHtyfQojIENhbGN1bGF0ZSBOb24temVybyB3aW5zL25vbWluYXRpb25zCnByaW50KHBhc3RlKCJOdW1iZXIgb2YgbW92aWVzIHdpdGggbm9uLXplcm8gd2lucyA9IixzdW0oZGZfNiRXaW5zICE9IDApKSkKcHJpbnQocGFzdGUoIk51bWJlciBvZiBtb3ZpZXMgd2l0aCBub24temVybyBub21pbmF0aW9ucyA9IixzdW0oZGZfNiROb21pbmF0aW9ucyAhPSAwKSkpCmBgYAoKCioqUSoqOiBIb3cgZGlkIHlvdSBjb25zdHJ1Y3QgeW91ciBjb252ZXJzaW9uIG1lY2hhbmlzbT8gSG93IG1hbnkgcm93cyBoYWQgdmFsaWQvbm9uLXplcm8gd2lucyBvciBub21pbmF0aW9ucz8KCioqQSoqOiBGaXJzdCBJIHVzZWQgdGhlIGRhdGEgZnJhbWUgd2UgdXNlZCBhZnRlciBjb25jbHVkaW5nIHF1ZXN0aW9uIDQuIE5leHQgSSBjcmVhdGVkIHRoZSBjb2x1bW5zICdXaW5zJyBhbmQgJ05vbWluYXRpb25zJy4gIEZyb20gaGVyZSBJIGxvb3BlZCB0aHJvdWdoIHRoZSBkYXRhZnJhbWUgc2VhcmNoaW5nIGZvciA0IGRpZmZlcmVudCBzdWJzdHJpbmdzOiAiV29uICIsICJOb21pbmF0ZWQgZm9yICIsICIgd2luIiwgIiBub21pbmF0aW9uIi4gIElmIHRoZSBncmVwbCBmdWN0aW9uIHJldHVybmVkIHRydWUgb24gdGhlc2Ugc3RyaW5ncywgdGhlbiBJIHVzZWQgdGhlIGdzdWIgZnVuY3Rpb24gdG8gZXh0cmFjdCB0aGUgbnVtYmVyIGJlZm9yZSBvciBhZnRlciB0aGlzIHN0cmluZyBkZXBlbmRpbmcgb24gdGhlIHN0cmluZy4gIEkgdXNlZCB0aGlzIG51bWJlciB0byBhZGQgdG8gZWl0aGVyIHdpbnMgb3Igbm9taW5hdGlvbnMgdGhlbiBhZGRlZCB0aGUgdmFsdWVzIHRvIHRoZSBuZXcgY29sdW1ucy4gSWYgbm9uZSBvZiB0aGUgc3RyaW5ncyB3ZXJlIGZvdW5kLCB0aGUgdmFsdWVzIHJlbWFpbmVkIGF0IHplcm8uICBGaW5hbGx5LCBhZnRlciBkZWZpbmluZyB0aGUgbmV3IGNvbHVtbnMgd2l0aCB0aGUgZXh0cmFjdGVkIHZhbHVlcywgSSByZW1vdmVkIHRoZSBvbGQgJ0F3YXJkcycgY29sdW1uLiBJIGZvdW5kIHRoYXQgdGhlcmUgd2VyZSA5MDcxIG1vdmllcyB3aXRoIG5vbi16ZXJvIHdpbnMgYW5kIDk1MzQgbW92aWVzIHdpdGggbm9uLXplcm8gbm9taW5hdGlvbnMgb3V0IG9mIHRoZSBvcmlnaW5hbCAzNCw5NTIgbW92aWVzLgoKYGBge3J9CiMgVE9ETzogUGxvdCBHcm9zcyByZXZlbnVlIGFnYWluc3Qgd2lucyBhbmQgbm9taW5hdGlvbnMKZ2dwbG90KGRmXzYsIGFlcyh4ID0gTnVtYmVyLCB5ID0gR3Jvc3MsIGNvbG9yID0gTnVtYmVyKSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBOb21pbmF0aW9ucywgY29sID0gIk5vbWluYXRpb25zIiksIHNpemUgPSAwLjcpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gV2lucywgY29sID0gIldpbnMiKSwgc2l6ZSA9IDAuNykgKwogIHhsaW0oMCwyMDApICsgeWxpbSgwLDEuNWU5KSArCiAgbGFicyh0aXRsZSA9ICdHcm9zcyBSZXZlbnVlICgkKSB2cyBXaW5zL05vbWluYXRpb25zJykKCmBgYAoKKipRKio6IEhvdyBkb2VzIHRoZSBncm9zcyByZXZlbnVlIHZhcnkgYnkgbnVtYmVyIG9mIGF3YXJkcyB3b24gYW5kIG5vbWluYXRpb25zIHJlY2VpdmVkPwoKKipBKio6IEFmdGVyIGxvb2tpbmcgYXQgYm90aCBOb21pbmF0aW9ucyBhbmQgV2lucyBpbmRpdmlkdWFsbHksIEkgaGF2ZSBmb3VuZCB0aGF0IHRoZXJlIGlzIG5vdCBtdWNoIG9mIGEgY29ycmVsYXRpb24gdG8gbXkgc3VycGlzZS4gIEV2ZW4gYWZ0ZXIgdGFraW5nIGludG8gYWNjb3VudCB0aGF0IHRoZXJlIGFyZSBzZXZlcmFsIG1vdmllcyB0aGF0IHJlY2VpdmVkIG5vIGF3YXJkcywgcGxlbnR5IG9mIHRoZXNlIG1vdmllcyBzZWVtZWQgdG8gZ3Jvc3MgYSBsb3Qgb2YgbW9uZXkuICBJbiBmYWN0LCBpbiBtYW55IGNhc2VzLCB0aGVzZSBtb3ZpZXMgZ3Jvc3NlZCBhcm91bmQgdGhlIHNhbWUgYW1vdW50IChnaXZlIG9yIHRha2UpIGFzIHRob3NlIG1vdmllcyB3aG8gcmVjZWl2ZWQgYSBwbGV0aG9yYSBvZiBhd2FyZHMuCgojIyA3LiBNb3ZpZSByYXRpbmdzIGZyb20gSU1EYiBhbmQgUm90dGVuIFRvbWF0b2VzCgpUaGVyZSBhcmUgc2V2ZXJhbCB2YXJpYWJsZXMgdGhhdCBkZXNjcmliZSByYXRpbmdzLCBpbmNsdWRpbmcgSU1EYiByYXRpbmdzIChgaW1kYlJhdGluZ2AgcmVwcmVzZW50cyBhdmVyYWdlIHVzZXIgcmF0aW5ncyBhbmQgYGltZGJWb3Rlc2AgcmVwcmVzZW50cyB0aGUgbnVtYmVyIG9mIHVzZXIgcmF0aW5ncyksIGFuZCBtdWx0aXBsZSBSb3R0ZW4gVG9tYXRvZXMgcmF0aW5ncyAocmVwcmVzZW50ZWQgYnkgc2V2ZXJhbCB2YXJpYWJsZXMgcHJlLWZpeGVkIGJ5IGB0b21hdG9gKS4gUmVhZCB1cCBvbiBzdWNoIHJhdGluZ3Mgb24gdGhlIHdlYiAoZm9yIGV4YW1wbGUgW3JvdHRlbnRvbWF0b2VzLmNvbS9hYm91dF0oaHR0cHM6Ly93d3cucm90dGVudG9tYXRvZXMuY29tL2Fib3V0KSBhbmQgWyB3d3cuaW1kYi5jb20vaGVscC9zaG93X2xlYWY/dm90ZXN0b3BmYXFdKGh0dHA6Ly8gd3d3LmltZGIuY29tL2hlbHAvc2hvd19sZWFmP3ZvdGVzdG9wZmFxKSkuCgpJbnZlc3RpZ2F0ZSB0aGUgcGFpcndpc2UgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRoZXNlIGRpZmZlcmVudCBkZXNjcmlwdG9ycyB1c2luZyBncmFwaHMuCgpgYGB7cn0KIyBUT0RPOiBJbGx1c3RyYXRlIGhvdyByYXRpbmdzIGZyb20gSU1EYiBhbmQgUm90dGVuIFRvbWF0b2VzIGFyZSByZWxhdGVkCmRmID0gZGZfNgpnZ3Bsb3QoZGYsIGFlcyh4ID0gaW1kYlJhdGluZywgeSA9IHRvbWF0b01ldGVyLCBjb2xvciA9IHRvbWF0b1VzZXJNZXRlcikpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMC43KSArCiAgZ2VvbV9zbW9vdGgoKSArCiAgbGFicyh0aXRsZSA9ICdUb21hdG8gTWV0ZXIgKENyaXRpYyBTY29yZSkgdnMgSU1EQiBSYXRpbmcgKFVzZXIgU2NvcmUpJykKYGBgCgpgYGB7cn0KYm94cGxvdChkZiR0b21hdG9NZXRlciwgbWFpbiA9ICdSb3R0ZW4gVG9tYXRvZXMgQ3JpdGljcyBNZXRlciBCb3ggUGxvdCcpCmBgYAoKYGBge3J9CmJveHBsb3QoZGYkaW1kYlJhdGluZywgbWFpbiA9ICdSb3R0ZW4gVG9tYXRvZXMgQ3JpdGljcyBNZXRlciBCb3ggUGxvdCcpCmBgYAoKYGBge3J9CnByaW50KHBhc3RlKCdDcml0aWNzIG1lYW4gPScsbWVhbihkZiR0b21hdG9NZXRlciwgbmEucm0gPSBUUlVFKSkpCnByaW50KHBhc3RlKCdDcml0aWNzIG1lZGlhbiA9JyxtZWRpYW4oZGYkdG9tYXRvTWV0ZXIsIG5hLnJtID0gVFJVRSkpKQpwcmludChwYXN0ZSgnVXNlcnMgbWVhbiA9JyxtZWFuKGRmJGltZGJSYXRpbmcsIG5hLnJtID0gVFJVRSkpKQpwcmludChwYXN0ZSgnVXNlcnMgbWVkaWFuID0nLG1lZGlhbihkZiRpbWRiUmF0aW5nLCBuYS5ybSA9IFRSVUUpKSkKYGBgCgoKKipRKio6IENvbW1lbnQgb24gdGhlIHNpbWlsYXJpdGllcyBhbmQgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgdXNlciByYXRpbmdzIG9mIElNRGIgYW5kIHRoZSBjcml0aWNzIHJhdGluZ3Mgb2YgUm90dGVuIFRvbWF0b2VzLgoKKipBKio6IEFmdGVyIGRvaW5nIHNvbWUgcmVzZWFyY2gsIEkgZm91bmQgb3V0IHRoYXQgSU1EQiByYXRpbmdzIGFyZSBiYXNlZCBvZmYgb2YgYSByYXRpbmcgc3lzdGVtIHdoZXJlIG1vdmllcyBhcmUgcmF0ZWQgYnkgSU1EQiBtZW1iZXJzLiBBbnlvbmUgY2FuIGJlY29tZSBhbiBJTURCIG1lbWJlciBhbmQgdm90ZS4gUm90dGVuIFRvbWF0b2VzIGlzIGRpZmZlcmVudC4gIFJvdHRlbiBUb21hdG9lcyBoYXMgdHdvIG1haW4gYWdncmVnYXRlZCBzY29yZXMgdXNlZCB0byByYXRlIG1vdmllczogdGhlIFRvbWF0b01ldGVyIGFuZCB0aGUgQXVkaWVuY2UgVXNlciBNZXRlci4gVGhlIFRvbWF0b01ldGVyIHJhdGluZyBpcyBhICJ0cnVzdGVkIG1lYXN1cmVtZW50IG9mIG1vdmllIGFuZCBUViBwcm9ncmFtbWluZyBxdWFpbGl0eSIgcmF0ZWQgYnkgcHJvZmVzc2lvbmFsIGZpbG0gYW5kIHRlbGV2aXNpb24gY3JpdGljcy4gU2luY2UgSSB3ZSBhcmUgYXNrZWQgdG8gcmV2aWV3IHRoZSBzaW1pbGFyaXRpZXMgYW5kIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIElNREIgdXNlciByYXRpbmdzIGFuZCB0aGUgUm90dGVuIFRvbWF0b2VzIGNyaXRpY3MgcmF0aW5ncywgSSBwbG90dGVkICd0b21hdG9NZXRlcicgdnMgJ2ltZGJSYXRpbmcnLiBGb3IgZnVuLCBJIGFsc28gdGhyZXcgaW4gdGhlIEF1ZGllbmNlIFVzZXIgTWV0ZXIgc2NvcmVzICh0b21hdG9Vc2VyTWV0ZXIpIHJlcHJlc2VudGVkIGFzIGEgZmlsbGVkIGNvbG9yIChzZWUgbGVnZW5kIGluIGdyYXBoIGFib3ZlKS4gQSBzaW1pbGFyaXR5IEkgZm91bmQgaW4gdGhlIHJlbGF0aW9uc2hpcCB3YXMgdGhhdCB0aGVyZSB3YXMgYW4gb2J2aW91cyBwb3NpdGl2ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHVzZXIgYW5kIGNyaXRpYyByYXRpbmdzLiBUaGVyZSB3ZXJlIHBsZW50eSBpZiBpbnRlcmVzdGluZyBkaWZmZXJlbmNlcyBhbHNvLiAgSXQgc2VlbXMgdGhlIHVzZXIgcmF0aW5ncyBhcmUgdHlwaWNhbGx5IGNvbnNlcnZhdGl2ZTsgbWVhbmluZyB0aGF0IHRoZSBtYWpvcml0eSBvZiB0aGVzZSByYXRpbmdzIGFyZSBiZXR3ZWVuIDU1JSBhbmQgNzUlIG9mIHRoZSBtYXhpbXVtIHJhdGluZy4gIE9uIHRoZSBvdGhlciBoYW5kLCBSb3R0ZW4gVG9tYXRvZXMgc2NvcmVzIGhhdmUgYSBtYWpvcml0eSByYW5nZSBiZXR3ZWVuIDMwJSBhbmQgODAlIHRoZSBtYXhpbXVtIHJhdGluZy4gIFRoYXQgaXMgb3ZlciBkb3VibGUgdGhlIHJhbmdlLiAgVGhlcmUgYXJlIG5vIG1vdmllcyB3aXRoIGEgMCBvciBwZXJmZWN0IHJhdGluZyBpbiBJTURCIGFuZCB0aGVyZSBhcmUgc2V2ZXJhbCBvZiBtb3ZpZXMgd2l0aCAwIG9yIHBlcmZlY3QgcmF0aW5ncyBpbiBjcml0aWNzIHJhdGluZ3MuIEFsc28sIGJhc2VkIG9uIHRoZSBtZWFuIGFuZCBtZWRpYW4gdmFsdWVzIGFib3ZlLCB1c2VycyBhcmUgdHlwaWNhbGx5ICJuaWNlciIgaW4gdGVybXMgb2YgZ2l2aW5nIG1vdmllcyBoaWdoIHJhdGluZ3Mgd2hpbGUgdGhlIGRhdGEgc2hvd3MgdGhhdCBjcml0aWNzIGFyZSBhIGJpdCB0b3VnaGVyLiBZb3UgY2FuIGFsc28gc2VlIHRoaXMgd2hlbiBhbmFseXppbmcgdGhlIGZpcnN0IHBsb3QgYXMgdGhlIG1lYW4gY3VydmUgaXMgc2hpZnRlZCB0byB0aGUgcmlnaHQgb2YgY2VudGVyLCByZXByZXNlbnRpbmcgaGlnaGVyIGF2ZXJhZ2UgdXNlciByYXRpbmdzIHRoYW4gY3JpdGljIHJhdGluZ3MuCgojIyA4LiBSYXRpbmdzIGFuZCBhd2FyZHMKClRoZXNlIHJhdGluZ3MgdHlwaWNhbGx5IHJlZmxlY3QgdGhlIGdlbmVyYWwgYXBwZWFsIG9mIHRoZSBtb3ZpZSB0byB0aGUgcHVibGljIG9yIGdhdGhlciBvcGluaW9ucyBmcm9tIGEgbGFyZ2VyIGJvZHkgb2YgY3JpdGljcy4gV2hlcmVhcyBhd2FyZHMgYXJlIGdpdmVuIGJ5IHByb2Zlc3Npb25hbCBzb2NpZXRpZXMgdGhhdCBtYXkgZXZhbHVhdGUgYSBtb3ZpZSBvbiBzcGVjaWZpYyBhdHRyaWJ1dGVzLCBzdWNoIGFzIGFydGlzdGljIHBlcmZvcm1hbmNlLCBzY3JlZW5wbGF5LCBzb3VuZCBkZXNpZ24sIGV0Yy4KClN0dWR5IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiByYXRpbmdzIGFuZCBhd2FyZHMgdXNpbmcgZ3JhcGhzIChhd2FyZHMgaGVyZSByZWZlcnMgdG8gd2lucyBhbmQvb3Igbm9taW5hdGlvbnMpLiAKCmBgYHtyfQojIFRPRE86IFNob3cgaG93IHJhdGluZ3MgYW5kIGF3YXJkcyBhcmUgcmVsYXRlZApiYXNlX2JyZWFrcyA8LSBmdW5jdGlvbihuID0gMTApewogIGZ1bmN0aW9uKHgpewogICAgYXhpc1RpY2tzKGxvZzEwKHJhbmdlKHgsIG5hLnJtID0gVFJVRSkpLCBsb2cgPSBUUlVFLCBuID0gbikKICB9Cn0KCmJyZWFrcyA8LSBheFRpY2tzKHNpZGUgPSAyKQoKZ2dwbG90KGRmLCBhZXMoeCA9IGltZGJSYXRpbmcsIHkgPSBXaW5zKSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAwLjcpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gbG9nX3RyYW5zKCksIGJyZWFrcyA9IGJhc2VfYnJlYWtzKCksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBwcmV0dHlOdW0pICsKICBnZW9tX3Ntb290aCgpICsKICBsYWJzKHRpdGxlID0gJ1dpbnMgdnMgVXNlciBSYXRpbmdzJykKZ2dwbG90KGRmLCBhZXMoeCA9IHRvbWF0b01ldGVyLCB5ID0gV2lucykpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMC43KSArIAogIHNjYWxlX3lfY29udGludW91cyh0cmFucyA9IGxvZ190cmFucygpLCBicmVha3MgPSBiYXNlX2JyZWFrcygpLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcHJldHR5TnVtKSArCiAgZ2VvbV9zbW9vdGgoKSArCiAgbGFicyh0aXRsZSA9ICdXaW5zIHZzIENyaXRpYyBSYXRpbmdzJykKZ2dwbG90KGRmLCBhZXMoeCA9IGltZGJSYXRpbmcsIHkgPSBOb21pbmF0aW9ucykpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMC43KSArIAogIHNjYWxlX3lfY29udGludW91cyh0cmFucyA9IGxvZ190cmFucygpLCBicmVha3MgPSBiYXNlX2JyZWFrcygpLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcHJldHR5TnVtKSArCiAgZ2VvbV9zbW9vdGgoKSArCiAgbGFicyh0aXRsZSA9ICdOb21pbmF0aW9ucyB2cyBVc2VyIFJhdGluZ3MnKQpnZ3Bsb3QoZGYsIGFlcyh4ID0gdG9tYXRvTWV0ZXIsIHkgPSBOb21pbmF0aW9ucykpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMC43KSArIAogIHNjYWxlX3lfY29udGludW91cyh0cmFucyA9IGxvZ190cmFucygpLCBicmVha3MgPSBiYXNlX2JyZWFrcygpLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcHJldHR5TnVtKSArCiAgZ2VvbV9zbW9vdGgoKSArCiAgbGFicyh0aXRsZSA9ICdOb21pbmF0aW9ucyB2cyBDcml0aWMgUmF0aW5ncycpCnBhcihvbGQucGFyKQpgYGAKCioqUSoqOiBIb3cgZ29vZCBhcmUgdGhlc2UgcmF0aW5ncyBpbiB0ZXJtcyBvZiBwcmVkaWN0aW5nIHRoZSBzdWNjZXNzIG9mIGEgbW92aWUgaW4gd2lubmluZyBhd2FyZHMgb3Igbm9taW5hdGlvbnM/IElzIHRoZXJlIGEgaGlnaCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHR3byB2YXJpYWJsZXM/CgoqKkEqKjogQXQgZmlyc3QgSSBtYWRlIHRoZXNlIHBsb3RzIG9uIHRoZSBsaW5lYXIgc2NhbGUgYW5kIGRpZCBub3Qgc2VlIG11Y2ggb2YgYSBjb3JyZWxhdGlvbi4gIFdoZW4gSSBjaGFuZ2VkIHRoZSB5IGF4aXMgdG8gdGhlIGxvZyBzY2FsZSwgSSBub3RpY2VkIGEgbGl0dGxlIG1vcmUgb2YgY29ycmVsZWF0aW9uLiAgSSBmaXJzdCB0aG91Z2h0IFRoZSByYXRpbmdzIGRpZG4ndCBkbyBhcyBnb29kIGEgam9iIHByZWRpY3RpbmcgYXdhcmQgd2lucyBvciBub21pbmF0aW9ucy4gIEJ1dCBJIGRpZCBiZWxpZXZlIHRoZSBvcHBvc2l0ZS4gSWYgYSBtb3ZpZSBoYWQgYXQgbGVhc3QgMzAgb3Igc28gYXdhcmRzLCB0aGVuIGl0IHdpbGwgbW9zdCBsaWtlbHkgaGF2ZSBhIHZlcnkgZ29vZCByYXRpbmcgKFVzZXIgYW5kIENyaXRpYyByYXRpbmcpLiAgVGhlIHNhbWUgY2FuIGJlIHNhaWQgaWYgYSBtb3ZpZSBoYXMgYXQgbGVhc3QgNjAgb3Igc28gbm9taW5hdGlvbnMuICBBZnRlciBjaGFuZ2luZyB0aGUgcGxvdHMgdG8geC1sb2cgc2NhbGUsIEkgbm90aWNlZCBhIHNsaWdodCBjb3JyZWxhdGlvbi4gIElmIHRoZSBpbWRiUmF0aW5nIGlzIGdyZWF0ZXIgdGhhbiA3LjUsIGl0IG1lYW5zIHRoZXNlIG1vdmllcyBoYXZlIG1vcmUgdGhhbiB0d28gdGltZXMgdGhlIG51bWJlciBvZiB3aW5zIGFuZCBub21pbmF0aW9ucyB0aGFuIHRob3NlIHdobyBoYXZlIGEgNS4wIHJhdGluZy4gIFRoZSBjcml0aWNzIHNjb3JlcyBoYWQgYW4gZXZlbiBoaWdoZXIgY29ycmVsYXRpb24gKGZvciB0aGUgbW9zdCBwYXJ0KS4gSWYgdGhlIE1ldGVyIHJhdGluZyB3YXMgaGlnaGVyIHRoYW4gODAsIGl0IGhhZCBhbG1vc3QgMyB0aW1lcyBtb3JlIGF3YXJkcyBhbiBhdmVyYWdlIHRoYW4gbW92aWVzIHdoaWNoIGhhZCByYXRpbmdzIGxvd2VyIHRoYW4gNTAuIFRoZSBzaW1pbGFyIGNvcnJlbGF0aW9uIGNhbiBiZSBpbnRlcnByZXR0ZWQgd2hlbiBsb29raW5nIGF0IG5vbWluYXRpb25zLiAgQW4gaW50ZXJlc3RpbmcgdHJlbmQgd2FzIHRoYXQgb25jZSB0aGUgTWV0ZXIgc2NvcmUgcmVhY2hlZCA5MCUsIHRoZSBudW1iZXIgb2Ygd2lucyBhbmQgbm9taW5hdGlvbnMgcGVha2VkIG9uIGF2ZXJhZ2UgYW5kIHRoZW4gcHJvY2VlZGVkIHRvIGRlY2xpbmUgYXMgdGhlIG1ldGVyIHNjb3JlIHJvc2UuIEkgd291bGRuJ3QgZ28gYW5kIHNheSB0aGVzZSByYXRpbmdzIGFyZSBnb29kIGF0IHByZWRpY3Rpbmcgc3VjY2VzcyBvZiBhIG1vdmllIHdpbm5pbmcgYXdhcmRzIG9yIG5vbWluYXRpb25zLCBidXQgSSB3b3VsZCBkZWZpbnRlbHkgc2F5IGhpZ2hlciByYXRpbmdzIG1lYW4gYSBtdWNoIG1vcmUgbGlrZWx5aG9vZCBvZiBzdWNjZXNzIG9uIGFuIGF3YXJkcyBjYXRlZ29yeS4KCiMjIDkuIEV4cGVjdGVkIGluc2lnaHRzCgpDb21lIHVwIHdpdGggdHdvIG5ldyBpbnNpZ2h0cyAoYmFja2VkIHVwIGJ5IGRhdGEgYW5kIGdyYXBocykgdGhhdCBpcyBleHBlY3RlZC4gSGVyZSDigJxuZXfigJ0gbWVhbnMgaW5zaWdodHMgdGhhdCBhcmUgbm90IGFuIGltbWVkaWF0ZSBjb25zZXF1ZW5jZSBvZiBvbmUgb2YgdGhlIGFib3ZlIHRhc2tzLiBZb3UgbWF5IHVzZSBhbnkgb2YgdGhlIGNvbHVtbnMgYWxyZWFkeSBleHBsb3JlZCBhYm92ZSBvciBhIGRpZmZlcmVudCBvbmUgaW4gdGhlIGRhdGFzZXQsIHN1Y2ggYXMgYFRpdGxlYCwgYEFjdG9yc2AsIGV0Yy4KCmBgYHtyfQojIFRPRE86IEZpbmQgYW5kIGlsbHVzdHJhdGUgdHdvIGV4cGVjdGVkIGluc2lnaHRzCiMgUG9wdWxhcmx5IGtub3duIHJhdGluZ3MKc3VtX3BvcFJhdGluZ3MgPSBzdW0oZGYkUmF0ZWQgPT0gIkciIHwgZGYkUmF0ZWQgPT0gIlBHIiB8IGRmJFJhdGVkID09ICJQRy0xMyIgfCBkZiRSYXRlZCA9PSAiUiIpClJhdGVfaW5kID0gZGYkUmF0ZWQgPT0gIkciIHwgZGYkUmF0ZWQgPT0gIlBHIiB8IGRmJFJhdGVkID09ICJQRy0xMyIgfCBkZiRSYXRlZCA9PSAiUiIKUmF0ZSA9IGRmW1JhdGVfaW5kLF0KZ2dwbG90KFJhdGUsIGFlcyh4ID0gUmF0ZWQsIHkgPSBHcm9zcywgZmlsbCA9IFJhdGVkKSkgKwogIGdlb21fYm94cGxvdCgpICsKICB5bGltKDAsNWU4KSArCiAgbGFicyh0aXRsZSA9ICdHcm9zcyB2cyBQb3B1bGFybHkgUmF0ZWQgRGlzdHJpYnV0aW9uIEJveCBQbG90cycpCmdncGxvdChSYXRlLCBhZXMoeCA9IFJhdGVkLCB5ID0gUnVudGltZSwgZmlsbCA9IFJhdGVkKSkgKwogIGdlb21fYm94cGxvdCgpICsKICB5bGltKDAsMjUwKSArCiAgbGFicyh0aXRsZSA9ICdSdW50aW1lIHZzIFBvcHVsYXJseSBSYXRlZCBEaXN0cmlidXRpb24gQm94IFBsb3RzJykKcHJpbnQocGFzdGUoIlBlcmNlbnRhZ2Ugb2YgbW92aWVzIHdpdGggcG9wdWxhciByYXRpbmdzIChHLCBQRywgUEctMTMsIFIpIGZyb20gb3JpZ2luYWwgbW92aWUgZGF0YXNldCA9IiwKICAgICAgICAgICAgcm91bmQoc3VtX3BvcFJhdGluZ3MvZGltKGRmXzMpWzFdLCAzKSoxMDAsICIlIikpCmBgYAoKKipRKio6IEV4cGVjdGVkIGluc2lnaHQgIzEuCgoqKkEqKjogIEFsbW9zdCBzdXJwcmlzaW5nbHksIGxlc3MgdGhhbiAzMCUgb2YgdGhlIG1vdmllcyAoZnJvbSBkZiBkYXRhc2V0KSBhcmUgcmF0ZWQgRywgUEcsIFBHLTEzLCBvciBSIHdoaWNoIGhhcyBiZWVuIGNvbnNpZGVyZWQgdGhlIG5vcm0gb2YgYSBtb3ZpZSByYXRpbmdzIHdoZW4gcmVsZWFzZWQgaW4gbW92aWUgdGhlYXRlcnMuLi4gQnV0LCBhcyBleHBlY3RlZCwgSSBkaWQgbm90IGJlbGlldmUgdGhlIHJhdGluZyBvZiB0aGUgbW92aWUgcmVhbGx5IGFmZmVjdGVkIHRoZSBydW50aW1lcy4gVGhlc2UgcnVudGltZXMgd2VyZSB1c2VkIHRvIGFuYWx5emUgR3Jvc3MgcmV2ZW51ZS4gQW5kIGNvbnNpc3RlbnQgd2l0aCB0aGUgcmVsZWF0aW9uc2hpcCBiZXR3ZWVuIHJ1bnRpbWVzIGFuZCBncm9zcyByZXZlbnVlLCByYXRlZCBkaWQgbm90IGhhdmUgYW4gb2J2aW91cyBpbXBhY3Qgb24gZ3Jvc3MgcmV2ZW51ZSBub3IgcnVudGltZS4gSXQgaXMgaW50ZXJlc3RpbmcgdGhhdCB0aGVzZSBvYnZpb3VzIGZpbmRzIGxlYWQgdG8gdW5vYnZpb3VzIGZpbmRpbmdzLiBUaGUgZGlzdHJpYnV0aW9ucyBmb3IgZXhwbGljaXQgbW92aWVzIGhhdmUgYSBuYXJyb3dlciBHcm9zcyBkaXN0cmlidXRpb24gdGhhbiBtb3JlICdmYW1pbHknIG1vdmllcy4KCmBgYHtyfQpnZ3Bsb3QoUmF0ZSwgYWVzKHggPSBSYXRlZCwgeSA9IFllYXIsIGZpbGwgPSBSYXRlZCkpICsKICBnZW9tX2JveHBsb3QoKSArCiAgbGFicyh0aXRsZSA9ICdZZWFyIHZzIFBvcHVsYXJseSBSYXRlZCBEaXN0cmlidXRpb24gQm94IFBsb3RzJykKCmdncGxvdChSYXRlLCBhZXMoeCA9IFllYXIsIHkgPSBXaW5zLCBjb2xvciA9IFJhdGVkKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNykgKwogIHNjYWxlX3lfY29udGludW91cyh0cmFucyA9IGxvZ190cmFucygpLCBicmVha3MgPSBiYXNlX2JyZWFrcygpLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcHJldHR5TnVtKSArCiAgeGxpbSgxOTUwLCAyMDE3KSArCiAgbGFicyh0aXRsZSA9ICdXaW5zIHZzIFllYXIgKFJhdGVkKScpCmBgYAoKCioqUSoqOiBFeHBlY3RlZCBpbnNpZ2h0ICMyLgoKKipBKio6IEFzIGV4cGVjdGVkLCBQRy0xMyBtb3ZpZXMgaGF2ZSB0aGUgbW9zdCAncmVjZW50JyB5ZWFyIGRpc3RyaWJ1dGlvbi4gVGhlIGJveCBwbG90IHNob3dzIHRoaXMgY2xlYXJseS4gIFBHLTEzIG1vdmllcyBhcmUgcmVsYXRpdmVseSBuZXcuIEFsc28sIGluIHRlcm1zIG9mIG51bWJlciBvZiB3aW5zLCBtb3N0IG9mIHRoZSBtb3ZpZXMgd2hpY2ggaGF2ZSB3b24gYXdhcmRzIHNpbmNlIDE5OTAgYXJlIHJhdGVkIFBHLTEzIG9yIFIuICBCZWZvcmUgUEctMTMgYmVjb21lIG1vcmUgcG9wdWxhciwgbW9zdCBvZiB0aGUgd2lucyB3ZXJlIGJ5IG1vdmllcyByYXRlZCBQRyBvciBSLiAgQWxzbywgdGhpcyBleHBsYWlucyB0aGF0IGlmIHlvdSB3YW50IHRvIHdpbiBhd2FyZHMsIGl0IGlzIG11Y2ggbW9yZSBkaWZmaWN1bHQgdG8gZG8gd2l0aCBhIEcgcmF0ZWQgbW92aWUgKHNvcnJ5IGtpZHMpLgoKCiMjIDEwLiBVbmV4cGVjdGVkIGluc2lnaHQKCkNvbWUgdXAgd2l0aCBvbmUgbmV3IGluc2lnaHQgKGJhY2tlZCB1cCBieSBkYXRhIGFuZCBncmFwaHMpIHRoYXQgaXMgdW5leHBlY3RlZCBhdCBmaXJzdCBnbGFuY2UgYW5kIGRvIHlvdXIgYmVzdCB0byBtb3RpdmF0ZSBpdC4gU2FtZSBpbnN0cnVjdGlvbnMgYXBwbHkgYXMgdGhlIHByZXZpb3VzIHRhc2suCgpgYGB7cn0KIyBUT0RPOiBGaW5kIGFuZCBpbGx1c3RyYXRlIG9uZSB1bmV4cGVjdGVkIGluc2lnaHQKIyBEYXRhID0gZGYgdXNlZCBpbiBwcm9ibGVtIDgKZ2dwbG90KGRmLCBhZXMoeCA9IFllYXIpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMwLCBjb2xvciA9ICdnb2xkJywgZmlsbCA9ICdibGFjaycpICsKICBsYWJzKHRpdGxlID0gJ1llYXIgUmVsZWFzZWQgSGlzdG9ncmFtJykgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICdkYXJrdHVycXVvaXNlJykpCmBgYAoKKipRKio6IFVuZXhwZWN0ZWQgaW5zaWdodC4KCioqQSoqOiBBdCBmaXJzdCBzaWdodCwgSSBiZWxlaXZlZCB0aGlzIHRvIGJlIGFuIGV4cGVjdGVkIGluc2lnaHQgYmVjYXVzZSBJIHRob3VnaHQgdGhlIG51bWJlciBvZiBtb3ZpZXMgbWFkZSB3b3VsZCBpbmNyZWFzZSBhcyB0aW1lIHBhc3NlZCBlc3BlY2lhbGx5IHNpbmNlIG1vdmllcyB0ZW5kIHRvIGNvbnRpbnVlIHRvIGJyZWFrIHJlY29yZHMgYXQgdGhlIGJveCBvZmZpY2UgYW5kIG1ha2UgbW9yZSBtb25leS4gQnV0IHRvIG15IHN1cnByaXNlLCBpdCBpcyBub3QgcXVpdGUgb2J2aW91cy4gU3VyZSB0aGVyZSB3YXMgYSBzcGlrZSBpbiB0aGUgbGF0ZSAxOTkwcyBhbmQgZWFybHkgMjAwMHMsIGJ1dCBpZiB5b3UgZXhjbHVkZSB0aGF0IHRpbWUgcGVyaW9kLCB0aGUgbnVtYmVyIG9mIG1vdmllcyByZWxlYXNlZCBwZXIgdGltZSBwZXJpb2QgaXMgcm91Z2hseSB0aGUgc2FtZSBhZnRlciBXb3JsZCBXYXIgSUkgZXJhLiAgCgo=